From c58bd33af7d7d236cb8056099a7a33a1c6bc7c8f Mon Sep 17 00:00:00 2001
From: Artem Panchenko <apanchenko@mirantis.com>
Date: Mon, 7 Nov 2016 22:37:12 +0200
Subject: [PATCH] Support new version of 'calicoctl' (>=v1.0.0)

Since version 'v1.0.0-beta' calicoctl is written
in Go and its API differs from old Python based
utility. Added support of both old and new version
of the utility.
---
 docs/calico.md                                | 24 +++++++
 roles/download/defaults/main.yml              |  3 +-
 roles/network_plugin/calico/tasks/main.yml    | 66 +++++++++++++++----
 .../calico/templates/calico-node.service.j2   | 12 +++-
 .../calico/templates/calicoctl-container.j2   |  2 +-
 .../calico/templates/deb-calico.initd.j2      | 11 +++-
 .../calico/templates/rh-calico.initd.j2       | 13 +++-
 7 files changed, 113 insertions(+), 18 deletions(-)

diff --git a/docs/calico.md b/docs/calico.md
index 50744f63f..a8bffc0db 100644
--- a/docs/calico.md
+++ b/docs/calico.md
@@ -10,18 +10,42 @@ docker ps | grep calico
 The **calicoctl** command allows to check the status of the network workloads.
 * Check the status of Calico nodes
 
+```
+calicoctl node status
+```
+
+or for versions prior *v1.0.0*:
+
 ```
 calicoctl status
 ```
 
 * Show the configured network subnet for containers
 
+```
+ calicoctl get ippool -o wide
+```
+
+or for versions prior *v1.0.0*:
+
 ```
 calicoctl pool show
 ```
 
 * Show the workloads (ip addresses of containers and their located)
 
+```
+calicoctl get workloadEndpoint -o wide
+```
+
+and
+
+```
+calicoctl get hostEndpoint -o wide
+```
+
+or for versions prior *v1.0.0*:
+
 ```
 calicoctl endpoint show --detail
 ```
diff --git a/roles/download/defaults/main.yml b/roles/download/defaults/main.yml
index bf8f8e7c2..1ea220fd1 100644
--- a/roles/download/defaults/main.yml
+++ b/roles/download/defaults/main.yml
@@ -39,7 +39,8 @@ flannel_server_helper_image_tag: "{{ flannel_server_helper_version }}"
 flannel_image_repo: "quay.io/coreos/flannel"
 flannel_image_tag: "{{ flannel_version }}"
 calicoctl_image_repo: "calico/ctl"
-# TODO(mattymo): v1.0.0-beta has different syntax. Needs work to upgrade
+# TODO(apanchenko): v1.0.0-beta can't execute `node run` from Docker container
+# for details see https://github.com/projectcalico/calico-containers/issues/1291
 calicoctl_image_tag: "v0.22.0"
 calico_node_image_repo: "calico/node"
 calico_node_image_tag: "{{ calico_version }}"
diff --git a/roles/network_plugin/calico/tasks/main.yml b/roles/network_plugin/calico/tasks/main.yml
index 60a728ba0..6563a1f65 100644
--- a/roles/network_plugin/calico/tasks/main.yml
+++ b/roles/network_plugin/calico/tasks/main.yml
@@ -78,30 +78,54 @@
   delegate_to: "{{groups['etcd'][0]}}"
   run_once: true
 
-- name: Calico | Define ipip pool argument
+- name: Calico | Check calicoctl version
+  run_once: true
+  set_fact:
+    legacy_calicoctl: "{{ calicoctl_image_tag | version_compare('v1.0.0', '<') }}"
+
+- name: Calico | Configure calico network pool
+  shell: >
+    echo '{
+    "kind": "ipPool",
+    "spec": {"disabled": false, "ipip": {"enabled": {{ cloud_provider is defined or ipip }}},
+             "nat-outgoing": {{ nat_outgoing|default(false) and not peer_with_router|default(false) }}},
+    "apiVersion": "v1",
+    "metadata": {"cidr": "{{ kube_pods_subnet }}"}
+    }'
+    | {{ bin_dir }}/calicoctl create -f -
+  environment:
+    NO_DEFAULT_POOLS: true
+  run_once: true
+  when: (not legacy_calicoctl and
+         "Key not found" in calico_conf.stdout or "nodes" not in calico_conf.stdout)
+
+- name: Calico (old) | Define ipip pool argument
   run_once: true
   set_fact:
     ipip_arg: "--ipip"
-  when: cloud_provider is defined or ipip|default(false)
+  when: (legacy_calicoctl and
+         cloud_provider is defined or ipip)
 
-- name: Calico | Define nat-outgoing pool argument
+- name: Calico (old) | Define nat-outgoing pool argument
   run_once: true
   set_fact:
     nat_arg: "--nat-outgoing"
-  when: nat_outgoing|default(false) and not peer_with_router|default(false)
+  when: (legacy_calicoctl and
+         nat_outgoing|default(false) and not peer_with_router|default(false))
 
-- name: Calico | Define calico pool task name
+- name: Calico (old) | Define calico pool task name
   run_once: true
   set_fact:
     pool_task_name: "with options {{ ipip_arg|default('') }} {{ nat_arg|default('') }}"
-  when: ipip_arg|default(false) or nat_arg|default(false)
+  when: (legacy_calicoctl and ipip_arg|default(false) or nat_arg|default(false))
 
-- name: Calico | Configure calico network pool {{ pool_task_name|default('') }}
+- name: Calico (old) | Configure calico network pool {{ pool_task_name|default('') }}
   command: "{{ bin_dir}}/calicoctl pool add {{ kube_pods_subnet }} {{ ipip_arg|default('') }} {{ nat_arg|default('') }}"
   environment:
     NO_DEFAULT_POOLS: true
   run_once: true
-  when: '"Key not found" in calico_conf.stdout or "nodes" not in calico_conf.stdout'
+  when: (legacy_calicoctl and
+         "Key not found" in calico_conf.stdout or "nodes" not in calico_conf.stdout)
 
 - name: Calico | Get calico configuration from etcd
   command: |-
@@ -154,10 +178,30 @@
     enabled: yes
 
 - name: Calico | Disable node mesh
-  shell: "{{ bin_dir }}/calicoctl bgp node-mesh off"
-  when: peer_with_router|default(false) and inventory_hostname in groups['kube-node']
+  shell: "{{ bin_dir }}/calicoctl config set nodeToNodeMesh off"
+  when: (not legacy_calicoctl and
+         peer_with_router|default(false) and inventory_hostname in groups['kube-node'])
 
 - name: Calico | Configure peering with router(s)
+  shell: >
+   echo '{
+   "kind": "bgppeer",
+   "spec": {"asNumber": {{ item.as }}},
+   "apiVersion": "v1",
+   "metadata": {"node": "rack1-host1", "scope": "node", "peerIP": "{{ item.router_id }}"}
+   }'
+   | {{ bin_dir }}/calicoctl create -f -
+  with_items: peers
+  when: (not legacy_calicoctl and
+         peer_with_router|default(false) and inventory_hostname in groups['kube-node'])
+
+- name: Calico (old) | Disable node mesh
+  shell: "{{ bin_dir }}/calicoctl bgp node-mesh off"
+  when: (legacy_calicoctl and
+         peer_with_router|default(false) and inventory_hostname in groups['kube-node'])
+
+- name: Calico (old) | Configure peering with router(s)
   shell: "{{ bin_dir }}/calicoctl node bgp peer add {{ item.router_id }} as {{ item.as }}"
   with_items: peers
-  when: peer_with_router|default(false) and inventory_hostname in groups['kube-node']
+  when: (legacy_calicoctl and
+         peer_with_router|default(false) and inventory_hostname in groups['kube-node'])
diff --git a/roles/network_plugin/calico/templates/calico-node.service.j2 b/roles/network_plugin/calico/templates/calico-node.service.j2
index 2a7775fd4..87a51fac8 100644
--- a/roles/network_plugin/calico/templates/calico-node.service.j2
+++ b/roles/network_plugin/calico/templates/calico-node.service.j2
@@ -7,11 +7,19 @@ Wants=docker.socket
 [Service]
 User=root
 PermissionsStartOnly=true
+{% if legacy_calicoctl %}
 {% if inventory_hostname in groups['kube-node'] and peer_with_router|default(false)%}
 ExecStart={{ bin_dir }}/calicoctl node --ip={{ip | default(ansible_default_ipv4.address) }} --as={{ local_as }} --detach=false --node-image={{ calico_node_image_repo }}:{{ calico_node_image_tag }}
-{%     else %}
+{% else %}
 ExecStart={{ bin_dir }}/calicoctl node --ip={{ip | default(ansible_default_ipv4.address) }} --detach=false --node-image={{ calico_node_image_repo }}:{{ calico_node_image_tag }}
-{%     endif %}
+{% endif %}
+{% else %}
+{% if inventory_hostname in groups['kube-node'] and peer_with_router|default(false)%}
+ExecStart={{ bin_dir }}/calicoctl node run --ip={{ip | default(ansible_default_ipv4.address) }} --as={{ local_as }} --node-image={{ calico_node_image_repo }}:{{ calico_node_image_tag }}
+{%     else %}
+ExecStart={{ bin_dir }}/calicoctl node run --ip={{ip | default(ansible_default_ipv4.address) }} --node-image={{ calico_node_image_repo }}:{{ calico_node_image_tag }}
+{% endif %}
+{% endif %}
 Restart=always
 RestartSec=10s
 
diff --git a/roles/network_plugin/calico/templates/calicoctl-container.j2 b/roles/network_plugin/calico/templates/calicoctl-container.j2
index 9d47c73ca..7be30928a 100644
--- a/roles/network_plugin/calico/templates/calicoctl-container.j2
+++ b/roles/network_plugin/calico/templates/calicoctl-container.j2
@@ -1,5 +1,5 @@
 #!/bin/bash
-/usr/bin/docker run --privileged --rm \
+/usr/bin/docker run -i --privileged --rm \
 --net=host --pid=host \
 -e ETCD_ENDPOINTS={{ etcd_access_endpoint }} \
 -e ETCD_CA_CERT_FILE=/etc/calico/certs/ca_cert.crt \
diff --git a/roles/network_plugin/calico/templates/deb-calico.initd.j2 b/roles/network_plugin/calico/templates/deb-calico.initd.j2
index ddbc22959..e155cae9c 100644
--- a/roles/network_plugin/calico/templates/deb-calico.initd.j2
+++ b/roles/network_plugin/calico/templates/deb-calico.initd.j2
@@ -37,7 +37,7 @@ DAEMON_USER=root
 
 do_status()
 {
-    if [ $($DOCKER ps | awk '{ print $2 }' | grep calico/node | wc -l) -eq 1 ]; then
+    if [ $($DOCKER ps --format "{{.Image}}" | grep -cw 'calico/node') -eq 1 ]; then
         return 0
     else
         return 1
@@ -51,7 +51,11 @@ do_start()
     do_status
     retval=$?
     if [ $retval -ne 0 ]; then
+{% if legacy_calicoctl %}
         ${DAEMON} node --ip=${DEFAULT_IPV4} >>/dev/null && return 0 || return 2
+{% else %}
+         ${DAEMON} node run --ip=${DEFAULT_IPV4} >>/dev/null && return 0 || return 2
+{% endif %}
     else
         return 1
     fi
@@ -62,7 +66,12 @@ do_start()
 #
 do_stop()
 {
+{% if legacy_calicoctl %}
     ${DAEMON} node stop >> /dev/null || ${DAEMON} node stop --force >> /dev/null
+{% else %}
+    echo "Current version of ${DAEMON} doesn't support 'node stop' command!"
+    return 1
+{% endif %}
 }
 
 
diff --git a/roles/network_plugin/calico/templates/rh-calico.initd.j2 b/roles/network_plugin/calico/templates/rh-calico.initd.j2
index 6fb870652..7fea72521 100644
--- a/roles/network_plugin/calico/templates/rh-calico.initd.j2
+++ b/roles/network_plugin/calico/templates/rh-calico.initd.j2
@@ -31,7 +31,7 @@ logfile="/var/log/$prog"
 
 do_status()
 {
-    if [ $($dockerexec ps | awk '{ print $2 }' | grep calico/node | wc -l) -ne 1 ]; then
+    if [ $($dockerexec ps --format "{{.Image}}" | grep -cw 'calico/node') -ne 1 ]; then
         return 1
     fi
 }
@@ -53,7 +53,11 @@ do_start() {
     if [ $retval -ne 0 ]; then
         printf "Starting $prog:\t"
         echo "\n$(date)\n" >> $logfile
-        $exec node --ip=${DEFAULT_IPV4} &>>$logfile 
+{% if legacy_calicoctl %}
+        $exec node --ip=${DEFAULT_IPV4} &>>$logfile
+{% else %}
+        $exec node run --ip=${DEFAULT_IPV4} &>>$logfile
+{% endif %}
         success
         echo
     else
@@ -65,7 +69,12 @@ do_start() {
 
 do_stop() {
     echo -n $"Stopping $prog: "
+{% if legacy_calicoctl %}
     $exec node stop >> /dev/null || $exec node stop --force >> /dev/null
+{% else %}
+    echo "Current version of ${exec} doesn't support 'node stop' command!"
+    return 1
+{% endif %}
     retval=$?
     echo
     return $retval
-- 
GitLab