From 9fba448053ae7070273ab980138ce6e1300bc344 Mon Sep 17 00:00:00 2001
From: Sascha Marcel Schmidt <mail@saschaschmidt.net>
Date: Wed, 8 Aug 2018 14:22:50 +0200
Subject: [PATCH] recator to use kube module, finally fix race condition in
 storage tasks

---
 .../tasks/{setup.yml => bootstrap.yml}        | 16 ++---
 .../provision/tasks/bootstrap/deploy.yml      | 23 +++++++
 .../tasks/{setup => bootstrap}/storage.yml    | 13 ++--
 .../tear-down.yml}                            |  0
 .../tasks/{setup => bootstrap}/topology.yml   |  1 +
 .../tasks/{setup => bootstrap}/volumes.yml    |  6 --
 .../roles/provision/tasks/glusterfs.yml       | 37 +++++++++++
 .../tasks/{kubernetes => glusterfs}/label.yml |  0
 .../heketi/roles/provision/tasks/heketi.yml   | 25 ++++++++
 .../roles/provision/tasks/kubernetes.yml      | 64 -------------------
 .../heketi/roles/provision/tasks/main.yml     | 45 ++++++-------
 .../heketi/roles/provision/tasks/secret.yml   | 27 ++++++++
 .../roles/provision/tasks/setup/boot.yml      | 27 --------
 .../roles/provision/tasks/setup/heketi.yml    | 10 ---
 .../heketi/roles/provision/tasks/storage.yml  | 11 ++++
 .../roles/provision/tasks/storageclass.yml    |  9 ++-
 .../heketi/roles/provision/tasks/topology.yml | 25 ++++++++
 .../templates/heketi-storage.json.j2          | 54 ++++++++++++++++
 18 files changed, 242 insertions(+), 151 deletions(-)
 rename contrib/network-storage/heketi/roles/provision/tasks/{setup.yml => bootstrap.yml} (89%)
 create mode 100644 contrib/network-storage/heketi/roles/provision/tasks/bootstrap/deploy.yml
 rename contrib/network-storage/heketi/roles/provision/tasks/{setup => bootstrap}/storage.yml (81%)
 rename contrib/network-storage/heketi/roles/provision/tasks/{setup/tear-down-bootstrap.yml => bootstrap/tear-down.yml} (100%)
 rename contrib/network-storage/heketi/roles/provision/tasks/{setup => bootstrap}/topology.yml (97%)
 rename contrib/network-storage/heketi/roles/provision/tasks/{setup => bootstrap}/volumes.yml (89%)
 create mode 100644 contrib/network-storage/heketi/roles/provision/tasks/glusterfs.yml
 rename contrib/network-storage/heketi/roles/provision/tasks/{kubernetes => glusterfs}/label.yml (100%)
 create mode 100644 contrib/network-storage/heketi/roles/provision/tasks/heketi.yml
 delete mode 100644 contrib/network-storage/heketi/roles/provision/tasks/kubernetes.yml
 create mode 100644 contrib/network-storage/heketi/roles/provision/tasks/secret.yml
 delete mode 100644 contrib/network-storage/heketi/roles/provision/tasks/setup/boot.yml
 delete mode 100644 contrib/network-storage/heketi/roles/provision/tasks/setup/heketi.yml
 create mode 100644 contrib/network-storage/heketi/roles/provision/tasks/storage.yml
 create mode 100644 contrib/network-storage/heketi/roles/provision/tasks/topology.yml
 create mode 100644 contrib/network-storage/heketi/roles/provision/templates/heketi-storage.json.j2

diff --git a/contrib/network-storage/heketi/roles/provision/tasks/setup.yml b/contrib/network-storage/heketi/roles/provision/tasks/bootstrap.yml
similarity index 89%
rename from contrib/network-storage/heketi/roles/provision/tasks/setup.yml
rename to contrib/network-storage/heketi/roles/provision/tasks/bootstrap.yml
index 96462dd11..a21cfadd6 100644
--- a/contrib/network-storage/heketi/roles/provision/tasks/setup.yml
+++ b/contrib/network-storage/heketi/roles/provision/tasks/bootstrap.yml
@@ -1,4 +1,3 @@
----
 # Bootstrap heketi
 - name: "Get state of heketi service, deployment and pods."
   register: "initial_heketi_state"
@@ -9,7 +8,7 @@
     - "(initial_heketi_state.stdout|from_json|json_query(\"items[?kind=='Service']\"))|length == 0"
     - "(initial_heketi_state.stdout|from_json|json_query(\"items[?kind=='Deployment']\"))|length == 0"
     - "(initial_heketi_state.stdout|from_json|json_query(\"items[?kind=='Pod']\"))|length == 0"
-  include_tasks: "setup/boot.yml"
+  include_tasks: "bootstrap/deploy.yml"
 
 # Prepare heketi topology
 - name: "Get heketi initial pod state."
@@ -27,11 +26,11 @@
   command: "{{ bin_dir }}/kubectl exec {{ initial_heketi_pod_name }} -- heketi-cli topology info --json"
 - name: "Load heketi topology."
   when: "heketi_topology.stdout|from_json|json_query(\"clusters[*].nodes[*]\")|flatten|length == 0"
-  include_tasks: "setup/topology.yml"
+  include_tasks: "bootstrap/topology.yml"
 
 # Provision heketi database volume
 - name: "Prepare heketi volumes."
-  include_tasks: "setup/volumes.yml"
+  include_tasks: "bootstrap/volumes.yml"
 
 # Prepare heketi storage
 - name: "Test heketi storage."
@@ -41,7 +40,7 @@
 - command: "{{ bin_dir }}/kubectl get secrets,endpoints,services,jobs --output=json"
   register: "job"
 - name: "Create heketi storage."
-  include_tasks: "setup/storage.yml"
+  include_tasks: "bootstrap/storage.yml"
   vars:
     secret_query: "items[?metadata.name=='heketi-storage-secret' && kind=='Secret']"
     endpoints_query: "items[?metadata.name=='heketi-storage-endpoints' && kind=='Endpoints']"
@@ -52,8 +51,7 @@
     - "heketi_storage_state.stdout|from_json|json_query(endpoints_query)|length == 0"
     - "heketi_storage_state.stdout|from_json|json_query(service_query)|length == 0"
     - "heketi_storage_state.stdout|from_json|json_query(job_query)|length == 0"
-# Finalize setup
+
+# Remove bootstrap heketi
 - name: "Tear down bootstrap."
-  include_tasks: "setup/tear-down-bootstrap.yml"
-- name: "Setup final heketi instance."
-  include_tasks: "setup/heketi.yml"
+  include_tasks: "bootstrap/tear-down.yml"
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/bootstrap/deploy.yml b/contrib/network-storage/heketi/roles/provision/tasks/bootstrap/deploy.yml
new file mode 100644
index 000000000..3580707d5
--- /dev/null
+++ b/contrib/network-storage/heketi/roles/provision/tasks/bootstrap/deploy.yml
@@ -0,0 +1,23 @@
+---
+- name: "Kubernetes Apps | Lay Down Heketi Bootstrap"
+  become: true
+  template: { src: "heketi-bootstrap.json.j2", dest: "{{ kube_config_dir }}/heketi-bootstrap.json" }
+  register: "rendering"
+- name: "Kubernetes Apps | Install and configure Heketi Bootstrap"
+  kube:
+    name: "GlusterFS"
+    filename: "{{ kube_config_dir }}/heketi-bootstrap.json"
+    state: "{{ rendering.changed | ternary('latest', 'present') }}"
+- name: "Wait for heketi bootstrap to complete."
+  changed_when: false
+  register: "initial_heketi_state"
+  vars:
+    initial_heketi_state: { stdout: "{}" }
+    pods_query: "items[?kind=='Pod'].status.conditions|[0][?type=='Ready'].status|[0]"
+    deployments_query: "items[?kind=='Deployment'].status.conditions|[0][?type=='Available'].status|[0]"
+  command: "{{ bin_dir }}/kubectl get services,deployments,pods --selector=deploy-heketi --output=json"
+  until:
+      - "initial_heketi_state.stdout|from_json|json_query(pods_query) == 'True'"
+      - "initial_heketi_state.stdout|from_json|json_query(deployments_query) == 'True'"
+  retries: 60
+  delay: 5
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/setup/storage.yml b/contrib/network-storage/heketi/roles/provision/tasks/bootstrap/storage.yml
similarity index 81%
rename from contrib/network-storage/heketi/roles/provision/tasks/setup/storage.yml
rename to contrib/network-storage/heketi/roles/provision/tasks/bootstrap/storage.yml
index 7c49a2258..1daa72ac1 100644
--- a/contrib/network-storage/heketi/roles/provision/tasks/setup/storage.yml
+++ b/contrib/network-storage/heketi/roles/provision/tasks/bootstrap/storage.yml
@@ -4,7 +4,10 @@
   changed_when: false
   register: "heketi_storage_state"
 - name: "Create heketi storage."
-  command: "{{ bin_dir }}/kubectl create -f {{ kube_config_dir }}/heketi-storage.json"
+  kube:
+    name: "GlusterFS"
+    filename: "{{ kube_config_dir }}/heketi-storage.json"
+    state: "present"
   vars:
     secret_query: "items[?metadata.name=='heketi-storage-secret' && kind=='Secret']"
     endpoints_query: "items[?metadata.name=='heketi-storage-endpoints' && kind=='Endpoints']"
@@ -16,17 +19,17 @@
     - "heketi_storage_state.stdout|from_json|json_query(service_query)|length == 0"
     - "heketi_storage_state.stdout|from_json|json_query(job_query)|length == 0"
   register: "heketi_storage_result"
-- command: "{{ bin_dir }}/kubectl get secrets,endpoints,services,jobs --output=json"
-  register: "heketi_storage_state"
+
 - name: "Get state of heketi storage service, endpoint, secret and job."
   command: "{{ bin_dir }}/kubectl get secrets,endpoints,services,jobs --output=json"
   changed_when: false
   register: "heketi_storage_state"
   vars:
+    heketi_storage_state: { stdout: "{}" }
     secret_query: "items[?metadata.name=='heketi-storage-secret' && kind=='Secret']"
     endpoints_query: "items[?metadata.name=='heketi-storage-endpoints' && kind=='Endpoints']"
     service_query: "items[?metadata.name=='heketi-storage-endpoints' && kind=='Service']"
-    job_query: "items[?metadata.name=='heketi-storage-copy-job' && kind=='Job' && status.conditions[?type=='Complete'].status=='True']"
+    job_query: "items[?metadata.name=='heketi-storage-copy-job' && kind=='Job' && status.succeeded==1]"
   until:
     - "heketi_storage_state.stdout|from_json|json_query(secret_query)|length == 1"
     - "heketi_storage_state.stdout|from_json|json_query(endpoints_query)|length == 1"
@@ -34,5 +37,3 @@
     - "heketi_storage_state.stdout|from_json|json_query(job_query)|length == 1"
   retries: 60
   delay: 5
-# looks like there is some race condition that leads to "Database file did not appear, exiting.", can't figure out where
-- command: "sleep 10"
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/setup/tear-down-bootstrap.yml b/contrib/network-storage/heketi/roles/provision/tasks/bootstrap/tear-down.yml
similarity index 100%
rename from contrib/network-storage/heketi/roles/provision/tasks/setup/tear-down-bootstrap.yml
rename to contrib/network-storage/heketi/roles/provision/tasks/bootstrap/tear-down.yml
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/setup/topology.yml b/contrib/network-storage/heketi/roles/provision/tasks/bootstrap/topology.yml
similarity index 97%
rename from contrib/network-storage/heketi/roles/provision/tasks/setup/topology.yml
rename to contrib/network-storage/heketi/roles/provision/tasks/bootstrap/topology.yml
index fd148d8d9..8c29aa1a6 100644
--- a/contrib/network-storage/heketi/roles/provision/tasks/setup/topology.yml
+++ b/contrib/network-storage/heketi/roles/provision/tasks/bootstrap/topology.yml
@@ -13,6 +13,7 @@
 - name: "Load heketi topology."
   when: "heketi_topology.stdout|from_json|json_query(\"clusters[*].nodes[*]\")|flatten|length == 0"
   command: "{{ bin_dir }}/kubectl exec {{ initial_heketi_pod_name }} -- heketi-cli topology load --json=/tmp/topology.json"
+  register: "load_heketi"
 - name: "Get heketi topology."
   register: "heketi_topology"
   command: "{{ bin_dir }}/kubectl exec {{ initial_heketi_pod_name }} -- heketi-cli topology info --json"
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/setup/volumes.yml b/contrib/network-storage/heketi/roles/provision/tasks/bootstrap/volumes.yml
similarity index 89%
rename from contrib/network-storage/heketi/roles/provision/tasks/setup/volumes.yml
rename to contrib/network-storage/heketi/roles/provision/tasks/bootstrap/volumes.yml
index 0d24a3a0b..e6226a7c3 100644
--- a/contrib/network-storage/heketi/roles/provision/tasks/setup/volumes.yml
+++ b/contrib/network-storage/heketi/roles/provision/tasks/bootstrap/volumes.yml
@@ -31,12 +31,6 @@
   with_items: "{{ heketi_volumes.stdout|from_json|json_query(\"volumes[*]\") }}"
   loop_control: { loop_var: "volume_id" }
   register: "volumes_information"
-- name: "debug heketi db vol."
-  with_items: "{{ volumes_information.results }}"
-  loop_control: { loop_var: "volume_information" }
-  vars: { volume: "{{ volume_information.stdout|from_json }}" }
-  when: "volume.name == 'heketidbstorage'"
-  debug: { var: "volume" }
 - name: "Test heketi database volume."
   set_fact: { heketi_database_volume_created: true }
   with_items: "{{ volumes_information.results }}"
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/glusterfs.yml b/contrib/network-storage/heketi/roles/provision/tasks/glusterfs.yml
new file mode 100644
index 000000000..e9650276a
--- /dev/null
+++ b/contrib/network-storage/heketi/roles/provision/tasks/glusterfs.yml
@@ -0,0 +1,37 @@
+---
+- name: "Kubernetes Apps | Lay Down GlusterFS Daemonset"
+  template: { src: "glusterfs-daemonset.json.j2", dest: "{{ kube_config_dir }}/glusterfs-daemonset.json" }
+  become: true
+  register: "rendering"
+- name: "Kubernetes Apps | Install and configure GlusterFS daemonset"
+  kube:
+    name: "GlusterFS"
+    filename: "{{ kube_config_dir }}/glusterfs-daemonset.json"
+    state: "{{ rendering.changed | ternary('latest', 'present') }}"
+- name: "Kubernetes Apps | Wait for daemonset to become available."
+  register: "daemonset_state"
+  command: "{{ bin_dir }}/kubectl get daemonset glusterfs --output=json --ignore-not-found=true"
+  changed_when: false
+  vars:
+    daemonset_state: { stdout: "{}" }
+    ready: "{{ daemonset_state.stdout|from_json|json_query(\"status.numberReady\") }}"
+    desired: "{{ daemonset_state.stdout|from_json|json_query(\"status.desiredNumberScheduled\") }}"
+  until: "ready == desired"
+  retries: 60
+  delay: 5
+
+- name: "Kubernetes Apps | Label GlusterFS nodes"
+  include_tasks: "glusterfs/label.yml"
+  with_items: "{{ groups['heketi-node'] }}"
+  loop_control:
+    loop_var: "node"
+
+- name: "Kubernetes Apps | Lay Down Heketi Service Account"
+  template: { src: "heketi-service-account.json.j2", dest: "{{ kube_config_dir }}/heketi-service-account.json" }
+  become: true
+  register: "rendering"
+- name: "Kubernetes Apps | Install and configure Heketi Service Account"
+  kube:
+    name: "GlusterFS"
+    filename: "{{ kube_config_dir }}/heketi-service-account.json"
+    state: "{{ rendering.changed | ternary('latest', 'present') }}"
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/kubernetes/label.yml b/contrib/network-storage/heketi/roles/provision/tasks/glusterfs/label.yml
similarity index 100%
rename from contrib/network-storage/heketi/roles/provision/tasks/kubernetes/label.yml
rename to contrib/network-storage/heketi/roles/provision/tasks/glusterfs/label.yml
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/heketi.yml b/contrib/network-storage/heketi/roles/provision/tasks/heketi.yml
new file mode 100644
index 000000000..44016df5e
--- /dev/null
+++ b/contrib/network-storage/heketi/roles/provision/tasks/heketi.yml
@@ -0,0 +1,25 @@
+---
+- name: "Kubernetes Apps | Lay Down Heketi"
+  become: true
+  template: { src: "heketi-deployment.json.j2", dest: "{{ kube_config_dir }}/heketi-deployment.json" }
+  register: "rendering"
+- name: "Kubernetes Apps | Install and configure Heketi"
+  kube:
+    name: "GlusterFS"
+    filename: "{{ kube_config_dir }}/heketi-deployment.json"
+    state: "{{ rendering.changed | ternary('latest', 'present') }}"
+- name: "Ensure heketi is up and running."
+  changed_when: false
+  register: "heketi_state"
+  vars:
+    heketi_state: { stdout: "{}" }
+    pods_query: "items[?kind=='Pod'].status.conditions|[0][?type=='Ready'].status|[0]"
+    deployments_query: "items[?kind=='Deployment'].status.conditions|[0][?type=='Available'].status|[0]"
+  command: "{{ bin_dir }}/kubectl get deployments,pods --selector=glusterfs --output=json"
+  until:
+      - "heketi_state.stdout|from_json|json_query(pods_query) == 'True'"
+      - "heketi_state.stdout|from_json|json_query(deployments_query) == 'True'"
+  retries: 60
+  delay: 5
+- set_fact:
+    heketi_pod_name: "{{ heketi_state.stdout|from_json|json_query(\"items[?kind=='Pod'].metadata.name|[0]\") }}"
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/kubernetes.yml b/contrib/network-storage/heketi/roles/provision/tasks/kubernetes.yml
deleted file mode 100644
index 6ab92713b..000000000
--- a/contrib/network-storage/heketi/roles/provision/tasks/kubernetes.yml
+++ /dev/null
@@ -1,64 +0,0 @@
----
-- register: "daemonset_state"
-  command: "{{ bin_dir }}/kubectl get daemonset glusterfs -o=name --ignore-not-found=true"
-  changed_when: false
-- name: "Deploy the GlusterFS DaemonSet"
-  when: "daemonset_state.stdout == \"\""
-  command: "{{ bin_dir }}/kubectl create -f {{ kube_config_dir }}/glusterfs-daemonset.json"
-- register: "daemonset_state"
-  command: "{{ bin_dir }}/kubectl get daemonset glusterfs --output=json --ignore-not-found=true"
-  changed_when: false
-- name: "Wait for daemonset to become available."
-  register: "daemonset_state"
-  command: "{{ bin_dir }}/kubectl get daemonset glusterfs --output=json --ignore-not-found=true"
-  changed_when: false
-  vars:
-    ready: "{{ daemonset_state.stdout|from_json|json_query(\"status.numberReady\") }}"
-    desired: "{{ daemonset_state.stdout|from_json|json_query(\"status.desiredNumberScheduled\") }}"
-  until: "ready == desired"
-  retries: 60
-  delay: 5
-
-- name: "Label Gluster nodes"
-  with_items: "{{ groups['heketi-node'] }}"
-  loop_control:
-    loop_var: "node"
-  include_tasks: "kubernetes/label.yml"
-
-- register: "service_account_state"
-  command: "{{ bin_dir }}/kubectl get serviceaccount heketi-service-account -o=name --ignore-not-found=true"
-  changed_when: false
-- name: "Deploy the Heketi service account"
-  when: "service_account_state.stdout == \"\""
-  command: "{{ bin_dir }}/kubectl create -f {{ kube_config_dir }}/heketi-service-account.json"
-- register: "service_account_state"
-  command: "{{ bin_dir }}/kubectl get serviceaccount heketi-service-account -o=name --ignore-not-found=true"
-  changed_when: false
-- assert: { that: "service_account_state.stdout != \"\"", message: "Heketi service account is not present." }
-
-- register: "clusterrolebinding_state"
-  command: "{{ bin_dir }}/kubectl get clusterrolebinding heketi-gluster-admin -o=name --ignore-not-found=true"
-  changed_when: false
-- name: "Deploy cluster role binding."
-  when: "clusterrolebinding_state.stdout == \"\""
-  command: "{{ bin_dir }}/kubectl create clusterrolebinding heketi-gluster-admin --clusterrole=edit --serviceaccount=default:heketi-service-account"
-- register: "clusterrolebinding_state"
-  command: "{{ bin_dir }}/kubectl get clusterrolebinding heketi-gluster-admin -o=name --ignore-not-found=true"
-  changed_when: false
-- assert: { that: "clusterrolebinding_state.stdout != \"\"", message: "Cluster role binding is not present." }
-
-- register: "secret_state"
-  command: "{{ bin_dir }}/kubectl get secret heketi-config-secret -o=name --ignore-not-found=true"
-  changed_when: false
-- name: "Render Heketi secret configuration."
-  become: true
-  template:
-    src: "heketi.json.j2"
-    dest: "{{ kube_config_dir }}/heketi.json"
-- name: "Deploy Heketi config secret"
-  when: "secret_state.stdout == \"\""
-  command: "{{ bin_dir }}/kubectl create secret generic heketi-config-secret --from-file={{ kube_config_dir }}/heketi.json"
-- register: "secret_state"
-  command: "{{ bin_dir }}/kubectl get secret heketi-config-secret -o=name --ignore-not-found=true"
-  changed_when: false
-- assert: { that: "secret_state.stdout != \"\"", message: "Heketi config secret is not present." }
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/main.yml b/contrib/network-storage/heketi/roles/provision/tasks/main.yml
index c247929a7..cae035893 100644
--- a/contrib/network-storage/heketi/roles/provision/tasks/main.yml
+++ b/contrib/network-storage/heketi/roles/provision/tasks/main.yml
@@ -1,34 +1,27 @@
 ---
-- name: "Render configuration."
-  become: true
-  template: { src: "{{ item.file }}.j2", dest: "{{ kube_config_dir }}/{{ item.file }}" }
-  with_items:
-      - { file: "glusterfs-daemonset.json" }
-      - { file: "heketi-bootstrap.json" }
-      - { file: "heketi-deployment.json" }
-      - { file: "heketi-service-account.json" }
-- name: "Prepare kubernetes."
-  include_tasks: "kubernetes.yml"
+- name: "Kubernetes Apps | GlusterFS"
+  include_tasks: "glusterfs.yml"
 
-- name: "Test heketi setup."
+- name: "Kubernetes Apps | Heketi Secrets"
+  include_tasks: "secret.yml"
+
+- name: "Kubernetes Apps | Test Heketi"
   register: "heketi_service_state"
-  command: "{{ bin_dir }}/kubectl get service heketi -o=name --ignore-not-found=true"
+  command: "kubectl get service heketi-storage-endpoints -o=name --ignore-not-found=true"
   changed_when: false
 
-- name: "Setup heketi."
+- name: "Kubernetes Apps | Bootstrap Heketi"
   when: "heketi_service_state.stdout == \"\""
-  include_tasks: "setup.yml"
+  include_tasks: "bootstrap.yml"
 
-- name: "Test storage class."
-  changed_when: false
-  command: "{{ bin_dir }}/kubectl get storageclass gluster --ignore-not-found=true --output=json"
-  register: "storageclass"
-- name: "Setup storage class."
-  when: "storageclass.stdout == \"\""
+- name: "Kubernetes Apps | Heketi"
+  include_tasks: "heketi.yml"
+
+- name: "Kubernetes Apps | Heketi Topology"
+  include_tasks: "topology.yml"
+
+- name: "Kubernetes Apps | Heketi Storage"
+  include_tasks: "storage.yml"
+
+- name: "Kubernetes Apps | Storage Class"
   include_tasks: "storageclass.yml"
-- name: "Test storage class."
-  changed_when: false
-  command: "{{ bin_dir }}/kubectl get storageclass gluster --ignore-not-found=true --output=json"
-  register: "storageclass"
-- name: "Ensure storage class is up."
-  assert: { that: "storageclass.stdout != \"\"" }
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/secret.yml b/contrib/network-storage/heketi/roles/provision/tasks/secret.yml
new file mode 100644
index 000000000..699eaf34b
--- /dev/null
+++ b/contrib/network-storage/heketi/roles/provision/tasks/secret.yml
@@ -0,0 +1,27 @@
+---
+- register: "clusterrolebinding_state"
+  command: "kubectl get clusterrolebinding heketi-gluster-admin -o=name --ignore-not-found=true"
+  changed_when: false
+- name: "Kubernetes Apps | Deploy cluster role binding."
+  when: "clusterrolebinding_state.stdout == \"\""
+  command: "kubectl create clusterrolebinding heketi-gluster-admin --clusterrole=edit --serviceaccount=default:heketi-service-account"
+- register: "clusterrolebinding_state"
+  command: "kubectl get clusterrolebinding heketi-gluster-admin -o=name --ignore-not-found=true"
+  changed_when: false
+- assert: { that: "clusterrolebinding_state.stdout != \"\"", message: "Cluster role binding is not present." }
+
+- register: "secret_state"
+  command: "kubectl get secret heketi-config-secret -o=name --ignore-not-found=true"
+  changed_when: false
+- name: "Render Heketi secret configuration."
+  become: true
+  template:
+    src: "heketi.json.j2"
+    dest: "{{ kube_config_dir }}/heketi.json"
+- name: "Deploy Heketi config secret"
+  when: "secret_state.stdout == \"\""
+  command: "kubectl create secret generic heketi-config-secret --from-file={{ kube_config_dir }}/heketi.json"
+- register: "secret_state"
+  command: "kubectl get secret heketi-config-secret -o=name --ignore-not-found=true"
+  changed_when: false
+- assert: { that: "secret_state.stdout != \"\"", message: "Heketi config secret is not present." }
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/setup/boot.yml b/contrib/network-storage/heketi/roles/provision/tasks/setup/boot.yml
deleted file mode 100644
index 806e91570..000000000
--- a/contrib/network-storage/heketi/roles/provision/tasks/setup/boot.yml
+++ /dev/null
@@ -1,27 +0,0 @@
----
-- name: "Get state of heketi service, deployment and pods."
-  register: "initial_heketi_state"
-  changed_when: false
-  command: "{{ bin_dir }}/kubectl get services,deployments,pods --selector=deploy-heketi --output=json"
-- name: "Create Heketi initial service and deployment"
-  command: "{{ bin_dir }}/kubectl create -f {{ kube_config_dir }}/heketi-bootstrap.json"
-  when:
-    - "(initial_heketi_state.stdout|from_json|json_query(\"items[?kind=='Service']\"))|length == 0"
-    - "(initial_heketi_state.stdout|from_json|json_query(\"items[?kind=='Deployment']\"))|length == 0"
-    - "(initial_heketi_state.stdout|from_json|json_query(\"items[?kind=='Pod']\"))|length == 0"
-- name: "Get state of heketi service, deployment and pods."
-  register: "initial_heketi_state"
-  changed_when: false
-  command: "{{ bin_dir }}/kubectl get services,deployments,pods --selector=deploy-heketi --output=json"
-- name: "Wait for heketi bootstrap to complete."
-  changed_when: false
-  register: "initial_heketi_state"
-  vars:
-    pods_query: "items[?kind=='Pod'].status.conditions|[0][?type=='Ready'].status|[0]"
-    deployments_query: "items[?kind=='Deployment'].status.conditions|[0][?type=='Available'].status|[0]"
-  command: "{{ bin_dir }}/kubectl get services,deployments,pods --selector=deploy-heketi --output=json"
-  until:
-      - "initial_heketi_state.stdout|from_json|json_query(pods_query) == 'True'"
-      - "initial_heketi_state.stdout|from_json|json_query(deployments_query) == 'True'"
-  retries: 60
-  delay: 5
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/setup/heketi.yml b/contrib/network-storage/heketi/roles/provision/tasks/setup/heketi.yml
deleted file mode 100644
index c38b151ac..000000000
--- a/contrib/network-storage/heketi/roles/provision/tasks/setup/heketi.yml
+++ /dev/null
@@ -1,10 +0,0 @@
----
-- name: "Create long term Heketi instance."
-  command: "{{ bin_dir }}/kubectl create -f {{ kube_config_dir }}/heketi-deployment.json"
-- name: "Get heketi deployment state."
-  register: "heketi_deployment_state"
-  command: "{{ bin_dir }}/kubectl get deployment heketi -o=name --ignore-not-found=true"
-  changed_when: false
-- name: "Ensure heketi is up and running."
-  assert: { that: "heketi_deployment_state.stdout != \"\"", message: "Heketi deployment did not succeed." }
-
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/storage.yml b/contrib/network-storage/heketi/roles/provision/tasks/storage.yml
new file mode 100644
index 000000000..881084bbe
--- /dev/null
+++ b/contrib/network-storage/heketi/roles/provision/tasks/storage.yml
@@ -0,0 +1,11 @@
+---
+- name: "Kubernetes Apps | Lay Down Heketi Storage"
+  become: true
+  vars: { nodes: "{{ groups['heketi-node'] }}" }
+  template: { src: "heketi-storage.json.j2", dest: "{{ kube_config_dir }}/heketi-storage.json" }
+  register: "rendering"
+- name: "Kubernetes Apps | Install and configure Heketi Storage"
+  kube:
+    name: "GlusterFS"
+    filename: "{{ kube_config_dir }}/heketi-storage.json"
+    state: "{{ rendering.changed | ternary('latest', 'present') }}"
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/storageclass.yml b/contrib/network-storage/heketi/roles/provision/tasks/storageclass.yml
index 393d76e64..835dc78b2 100644
--- a/contrib/network-storage/heketi/roles/provision/tasks/storageclass.yml
+++ b/contrib/network-storage/heketi/roles/provision/tasks/storageclass.yml
@@ -16,6 +16,9 @@
   template:
     src: "storageclass.yml.j2"
     dest: "{{ kube_config_dir }}/storageclass.yml"
-- name: "Setup storage class."
-  when: "storageclass.stdout == \"\""
-  command: "{{ bin_dir }}/kubectl create -f {{ kube_config_dir }}/storageclass.yml"
+  register: "rendering"
+- name: "Kubernetes Apps | Install and configure Storace Class"
+  kube:
+    name: "GlusterFS"
+    filename: "{{ kube_config_dir }}/storageclass.yml"
+    state: "{{ rendering.changed | ternary('latest', 'present') }}"
diff --git a/contrib/network-storage/heketi/roles/provision/tasks/topology.yml b/contrib/network-storage/heketi/roles/provision/tasks/topology.yml
new file mode 100644
index 000000000..52c709f37
--- /dev/null
+++ b/contrib/network-storage/heketi/roles/provision/tasks/topology.yml
@@ -0,0 +1,25 @@
+---
+- name: "Get heketi topology."
+  register: "heketi_topology"
+  changed_when: false
+  command: "{{ bin_dir }}/kubectl exec {{ heketi_pod_name }} -- heketi-cli topology info --json"
+- name: "Render heketi topology template."
+  become: true
+  vars: { nodes: "{{ groups['heketi-node'] }}" }
+  register: "rendering"
+  template:
+    src: "topology.json.j2"
+    dest: "{{ kube_config_dir }}/topology.json"
+- name: "Copy topology configuration into container."
+  when: "rendering.changed"
+  command: "{{ bin_dir }}/kubectl cp {{ kube_config_dir }}/topology.json {{ heketi_pod_name }}:/tmp/topology.json"
+- name: "Load heketi topology."
+  when: "rendering.changed"
+  command: "{{ bin_dir }}/kubectl exec {{ heketi_pod_name }} -- heketi-cli topology load --json=/tmp/topology.json"
+- name: "Get heketi topology."
+  register: "heketi_topology"
+  changed_when: false
+  command: "{{ bin_dir }}/kubectl exec {{ heketi_pod_name }} -- heketi-cli topology info --json"
+  until: "heketi_topology.stdout|from_json|json_query(\"clusters[*].nodes[*].devices[?state=='online'].id\")|flatten|length == groups['heketi-node']|length"
+  retries: 60
+  delay: 5
diff --git a/contrib/network-storage/heketi/roles/provision/templates/heketi-storage.json.j2 b/contrib/network-storage/heketi/roles/provision/templates/heketi-storage.json.j2
new file mode 100644
index 000000000..3089256c9
--- /dev/null
+++ b/contrib/network-storage/heketi/roles/provision/templates/heketi-storage.json.j2
@@ -0,0 +1,54 @@
+{
+  "apiVersion": "v1",
+  "kind": "List",
+  "items": [
+    {
+      "kind": "Endpoints",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "heketi-storage-endpoints",
+        "creationTimestamp": null
+      },
+      "subsets": [
+{% set nodeblocks = [] %}
+{% for node in nodes %}
+{% set nodeblock %}
+        {
+          "addresses": [
+            {
+              "ip": "{{ hostvars[node]['ansible_facts']['default_ipv4']['address'] }}"
+            }
+          ],
+          "ports": [
+            {
+              "port": 1
+            }
+          ]
+        }
+{% endset %}
+{% if nodeblocks.append(nodeblock) %}{% endif %}
+{% endfor %}
+{{ nodeblocks|join(',') }}
+      ]
+    },
+    {
+      "kind": "Service",
+      "apiVersion": "v1",
+      "metadata": {
+        "name": "heketi-storage-endpoints",
+        "creationTimestamp": null
+      },
+      "spec": {
+        "ports": [
+          {
+            "port": 1,
+            "targetPort": 0
+          }
+        ]
+      },
+      "status": {
+        "loadBalancer": {}
+      }
+    }
+  ]
+}
-- 
GitLab