|
@@ -3,219 +3,198 @@
|
|
#
|
|
#
|
|
# The following variables must exist:
|
|
# The following variables must exist:
|
|
#
|
|
#
|
|
-# op:
|
|
|
|
-# desired_csv
|
|
|
|
-# op_nsp
|
|
|
|
-# op_cat
|
|
|
|
-# op_pkg
|
|
|
|
-# op_chn
|
|
|
|
-# op_mod
|
|
|
|
|
|
+# added_operators: (list)
|
|
|
|
+# - catalog: the catalog of the manifest
|
|
|
|
+# package: the name of the packagemanifest
|
|
|
|
+# subscription: the name of the operatorgroup and subscription (optional, defaults to package)
|
|
|
|
+# channel: which channel to install from
|
|
|
|
+# namespace: target namespace for subscription
|
|
|
|
+# desired_csv: for verification - wait for this CSV to appear
|
|
|
|
+# og_namespaces: (list) operatorgroup namespaces
|
|
|
|
+# approval: Automatic (default) or Manual
|
|
#
|
|
#
|
|
-# NOTE: Do NOT test by checking for presence of API resources - they do not always get cleaned up.
|
|
|
|
-
|
|
|
|
-- debug:
|
|
|
|
- msg: Deploying operator {{ op.op_pkg }} in namespace {{ op.op_nsp }}...
|
|
|
|
-
|
|
|
|
-# See if the CSV is available already (anywhere), meaning the operator is already installed.
|
|
|
|
-- name: Check if the CSV exists already
|
|
|
|
- k8s_info:
|
|
|
|
- kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
|
|
+# This role must then be applied as:
|
|
|
|
+#
|
|
|
|
+# - include_role:
|
|
|
|
+# name: deploy-operators
|
|
|
|
+# loop: "{{ added_operators }}"
|
|
|
|
+# loop_control:
|
|
|
|
+# loop_var: role
|
|
|
|
+#
|
|
|
|
+# What this means is that each item of added_operators is expected to be
|
|
|
|
+# placed in the "role" variable prior to iterating over this role.
|
|
|
|
+#
|
|
|
|
+# OPTIONAL:
|
|
|
|
+#
|
|
|
|
+# kubeadmin_config kubeadmin (or other admin) credentials (tmp/kubeconfig-ocp4)
|
|
|
|
+#
|
|
|
|
+# NOTE: Do NOT test by checking for presence of API resources - they do not
|
|
|
|
+# always get cleaned up, so it might report a false positive.
|
|
|
|
+#
|
|
|
|
+- name: Tell what is happening
|
|
|
|
+ ansible.builtin.prompt:
|
|
|
|
+ msg: |
|
|
|
|
+ *******************************************************************************
|
|
|
|
+ * Deploying operator {{ role.name }} to namespace {{ role.namespace }}
|
|
|
|
+ *******************************************************************************
|
|
|
|
+
|
|
|
|
+- name: Make sure the namespace is there
|
|
|
|
+ kubernetes.core.k8s:
|
|
|
|
+ kubeconfig: "{{ kubeadmin_config }}"
|
|
validate_certs: no
|
|
validate_certs: no
|
|
- api_version: operators.coreos.com/v1alpha1
|
|
|
|
- kind: clusterserviceversion
|
|
|
|
- register: all_csv
|
|
|
|
-
|
|
|
|
-- name: Find the wanted CSV among all CSVs
|
|
|
|
- set_fact:
|
|
|
|
- found_csv: '{{ (all_csv | community.general.json_query("resources[?contains(metadata.name, `" + op.op_pkg + ".`)]")) }}'
|
|
|
|
- when:
|
|
|
|
- - all_csv.resources is defined
|
|
|
|
- - (all_csv.resources | length) > 0
|
|
|
|
|
|
+ api_version: v1
|
|
|
|
+ kind: namespace
|
|
|
|
+ name: "{{ role.namespace }}"
|
|
|
|
|
|
-- name: Get details about the CSV if found
|
|
|
|
- set_fact:
|
|
|
|
- csv_ns: "{{ found_csv[0] | community.general.json_query('metadata.namespace') }}"
|
|
|
|
- csv_name: "{{ found_csv[0] | community.general.json_query('metadata.name') }}"
|
|
|
|
|
|
+- name: Verify if the namespace has an OperatorGroup already
|
|
|
|
+ kubernetes.core.k8s_info:
|
|
|
|
+ kubeconfig: "{{ kubeadmin_config }}"
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: operators.coreos.com/v1
|
|
|
|
+ kind: operatorgroup
|
|
|
|
+ namespace: "{{ role.namespace }}"
|
|
|
|
+ register: found_opgrp
|
|
|
|
+
|
|
|
|
+- name: Show the operator groups found at verbosity 2+
|
|
|
|
+ debug:
|
|
|
|
+ var: found_opgrp
|
|
|
|
+ verbosity: 2
|
|
|
|
+
|
|
|
|
+- name: The OperatorGroup must have a matching targetNamespaces if it exists already, or we can not continue.
|
|
|
|
+ ansible.builtin.assert:
|
|
|
|
+ that:
|
|
|
|
+ - >
|
|
|
|
+ (role.og_namespaces is defined and
|
|
|
|
+ found_opgrp.resources[0].spec.targetNamespaces is defined and
|
|
|
|
+ found_opgrp.resources[0].spec.targetNamespaces == role.og_namespaces) or
|
|
|
|
+ ((role.og_namespaces is not defined or role.og_namespaces == []) and
|
|
|
|
+ found_opgrp.resources[0].spec.targetNamespaces is not defined)
|
|
|
|
+ success_msg: "Found existing OperatorGroup with matching targetNamespaces."
|
|
|
|
+ fail_msg: "FATAL: OperatorGroup already exists but does not have matching targetNamespaces."
|
|
when:
|
|
when:
|
|
- - found_csv is defined
|
|
|
|
- - (found_csv | length) > 0
|
|
|
|
|
|
+ - found_opgrp.resources is defined
|
|
|
|
+ - found_opgrp.resources[0] is defined
|
|
|
|
+ - found_opgrp.resources[0].spec is defined
|
|
|
|
|
|
-- debug:
|
|
|
|
- msg: Operator {{ op.op_pkg }} is already installed in {{ csv_ns }} as {{ csv_name }} (desired_csv == {{ op.desired_csv }}).
|
|
|
|
|
|
+- name: If not there yet, create an OperatorGroup
|
|
|
|
+ kubernetes.core.k8s:
|
|
|
|
+ kubeconfig: "{{ kubeadmin_config }}"
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: operators.coreos.com/v1
|
|
|
|
+ kind: operatorgroup
|
|
|
|
+ namespace: "{{ role.namespace }}"
|
|
|
|
+ name: "{{ role.subscription | default(role.package) }}"
|
|
|
|
+ definition:
|
|
|
|
+ spec:
|
|
|
|
+ targetNamespaces: "{{ role.og_namespaces | default(omit) }}"
|
|
when:
|
|
when:
|
|
- - csv_ns is defined
|
|
|
|
- - csv_name is defined
|
|
|
|
|
|
+ - >
|
|
|
|
+ found_opgrp.resources is not defined or
|
|
|
|
+ found_opgrp.resources[0] is not defined
|
|
|
|
|
|
-- name: Only proceed here if the CSV was not found anywhere yet
|
|
|
|
|
|
+- name: Check if installPlans exist already before fondling around with subscriptions
|
|
|
|
+ kubernetes.core.k8s_info:
|
|
|
|
+ kubeconfig: "{{ kubeadmin_config }}"
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: operators.coreos.com/v1alpha1
|
|
|
|
+ kind: installplan
|
|
|
|
+ namespace: "{{ role.namespace }}"
|
|
|
|
+ register: installplan_pre
|
|
|
|
+
|
|
|
|
+- name: Remember the installPlan(s) found prior to fondling around with subscriptions as a fact
|
|
|
|
+ ansible.builtin.set_fact:
|
|
|
|
+ sub_ip_pre: "{{ installplan_pre.resources | selectattr('metadata.ownerReferences.0.name', 'in', (role.subscription | default(role.package))) | sort(attribute='metadata.creationTimestamp', reverse=True) }}"
|
|
|
|
+
|
|
|
|
+- name: Show the installplan(s) found at verbosity 2+
|
|
|
|
+ debug:
|
|
|
|
+ var: sub_ip_pre
|
|
|
|
+ verbosity: 2
|
|
|
|
+
|
|
|
|
+- name: Also make sure there is a subscription
|
|
|
|
+ kubernetes.core.k8s:
|
|
|
|
+ kubeconfig: "{{ kubeadmin_config }}"
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: operators.coreos.com/v1alpha1
|
|
|
|
+ kind: subscription
|
|
|
|
+ namespace: "{{ role.namespace }}"
|
|
|
|
+ name: "{{ role.subscription | default(role.package) }}"
|
|
|
|
+ definition:
|
|
|
|
+ spec:
|
|
|
|
+ source: "{{ role.catalog }}"
|
|
|
|
+ sourceNamespace: openshift-marketplace
|
|
|
|
+ name: "{{ role.package }}"
|
|
|
|
+ channel: "{{ role.channel }}"
|
|
|
|
+ startingCSV: "{{ role.desired_csv }}"
|
|
|
|
+ installPlanApproval: "{{ role.approval | default('Automatic') }}"
|
|
|
|
+ register: subscription
|
|
|
|
+
|
|
|
|
+- name: Handle installPlan if the subscription changed anything
|
|
block:
|
|
block:
|
|
-
|
|
|
|
- # Check if package provides desired_csv in any of the channels
|
|
|
|
- - name: Get packagemanifest
|
|
|
|
- k8s_info:
|
|
|
|
- kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
- validate_certs: no
|
|
|
|
- api_version: packages.operators.coreos.com/v1
|
|
|
|
- kind: packagemanifest
|
|
|
|
- namespace: openshift-marketplace
|
|
|
|
- name: "{{ op.op_pkg }}"
|
|
|
|
- register: pkg_mft
|
|
|
|
-
|
|
|
|
- - name: Search for any channels that provide desired_csv
|
|
|
|
- set_fact:
|
|
|
|
- found_chn: '{{ (pkg_mft | community.general.json_query("resources[0].status.channels[?currentCSV == `" + op.desired_csv + "`].name")) }}'
|
|
|
|
- when:
|
|
|
|
- - pkg_mft is defined
|
|
|
|
- - pkg_mft.resources | length > 0
|
|
|
|
-
|
|
|
|
- - name: Fail if no channel provides the desired_csv
|
|
|
|
- fail:
|
|
|
|
- msg: No {{ op.op_pkg }} channel provides {{ op.desired_csv }}
|
|
|
|
- when:
|
|
|
|
- - found_chn is defined
|
|
|
|
- - found_chn | length == 0
|
|
|
|
-
|
|
|
|
- - name: Fail if selected channel does not provide the desired_csv
|
|
|
|
- fail:
|
|
|
|
- msg: Operator {{ op.op_pkg }} selected channel {{ op.op_chn }} does not provide {{ op.desired_csv }} - {{ found_chn }}
|
|
|
|
- when:
|
|
|
|
- - found_chn is defined
|
|
|
|
- - found_chn | length > 0
|
|
|
|
- - op.op_chn not in found_chn
|
|
|
|
-
|
|
|
|
- # Create the namespace if necessary
|
|
|
|
- - name: Make sure the namespace is there
|
|
|
|
- k8s:
|
|
|
|
- kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
- validate_certs: no
|
|
|
|
- api_version: v1
|
|
|
|
- kind: namespace
|
|
|
|
- name: "{{ op.op_nsp }}"
|
|
|
|
-
|
|
|
|
- # Check for operator group - if existing, make sure it's compatible; otherwise create it
|
|
|
|
- - name: See if there are any OperatorGroups
|
|
|
|
- k8s_info:
|
|
|
|
- kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
- validate_certs: no
|
|
|
|
- api_version: operators.coreos.com/v1
|
|
|
|
- kind: operatorgroup
|
|
|
|
- namespace: "{{ op.op_nsp }}"
|
|
|
|
- register: found_opgrp
|
|
|
|
-
|
|
|
|
- - name: Die if more than one OG
|
|
|
|
- ansible.builtin.fail:
|
|
|
|
- msg: More than one OperatorGroup found in project.
|
|
|
|
- when: (found_opgrp.resources | length) > 1
|
|
|
|
-
|
|
|
|
- - name: Die if op_mod is not recognised
|
|
|
|
- ansible.builtin.fail:
|
|
|
|
- msg: Unrecognised op.op_mod ({{ op.op_mod }})
|
|
|
|
- when: op.op_mod not in ["all", "self"]
|
|
|
|
-
|
|
|
|
- - name: Die if op_mod does not match found OG mode
|
|
|
|
- ansible.builtin.fail:
|
|
|
|
- msg: Found OG mode does not match required ({{ op.op_mod }})
|
|
|
|
- when: |
|
|
|
|
- ((found_opgrp.resources | length) == 1 and op.op_mod == "all" and found_opgrp.resources[0].status.namespaces != [""]) or
|
|
|
|
- ((found_opgrp.resources | length) == 1 and op.op_mod == "self" and found_opgrp.resources[0].status.namespaces != [op.op_nsp])
|
|
|
|
-
|
|
|
|
- - name: Set fact for namespaces of OG if we require all
|
|
|
|
- set_fact:
|
|
|
|
- og_spec: []
|
|
|
|
- when:
|
|
|
|
- - op.op_mod == "all"
|
|
|
|
- - (found_opgrp.resources | length) == 0
|
|
|
|
-
|
|
|
|
- - name: Set fact for namespaces of OG if we require self
|
|
|
|
- set_fact:
|
|
|
|
- og_spec: ["{{op.op_nsp}}"]
|
|
|
|
- when:
|
|
|
|
- - op.op_mod == "self"
|
|
|
|
- - (found_opgrp.resources | length) == 0
|
|
|
|
-
|
|
|
|
- - name: Create the OperatorGroup if not there
|
|
|
|
- k8s:
|
|
|
|
- kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
- validate_certs: no
|
|
|
|
- api_version: operators.coreos.com/v1
|
|
|
|
- kind: operatorgroup
|
|
|
|
- namespace: "{{ op.op_nsp }}"
|
|
|
|
- name: "{{ op.op_nsp }}"
|
|
|
|
- definition:
|
|
|
|
- spec:
|
|
|
|
- targetNamespaces: "{{ og_spec }}"
|
|
|
|
- when: (found_opgrp.resources | length) == 0
|
|
|
|
-
|
|
|
|
- # create a subscription if not there, and wait for the CSV
|
|
|
|
- - name: Also make sure there is a subscription
|
|
|
|
- k8s:
|
|
|
|
- kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
- validate_certs: no
|
|
|
|
- api_version: operators.coreos.com/v1alpha1
|
|
|
|
- kind: subscription
|
|
|
|
- namespace: "{{ op.op_nsp }}"
|
|
|
|
- name: "{{ op.op_pkg }}"
|
|
|
|
- definition:
|
|
|
|
- spec:
|
|
|
|
- source: "{{ op.op_cat }}"
|
|
|
|
- sourceNamespace: openshift-marketplace
|
|
|
|
- name: "{{ op.op_pkg }}"
|
|
|
|
- channel: "{{ op.op_chn }}"
|
|
|
|
- installPlanApproval: Automatic
|
|
|
|
-
|
|
|
|
- # TODO: Finish this at some point.
|
|
|
|
- #- name: Wait for installPlan to show up
|
|
|
|
- # k8s_info:
|
|
|
|
- # kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
- # validate_certs: no
|
|
|
|
- # api_version: operators.coreos.com/v1alpha1
|
|
|
|
- # kind: installplan
|
|
|
|
- # namespace: "{{ op_nsp }}"
|
|
|
|
- # register: installplan
|
|
|
|
- # until:
|
|
|
|
- # - installplan.resources is defined
|
|
|
|
- # - (installplan.resources | length) > 0
|
|
|
|
- # - installplan.resources[0].spec.approved
|
|
|
|
- # retries: 12
|
|
|
|
- # delay: 10
|
|
|
|
-
|
|
|
|
- - name: Wait for CSV to show up and complete
|
|
|
|
- k8s_info:
|
|
|
|
- kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
|
|
+ - name: Wait for installPlan to show up
|
|
|
|
+ kubernetes.core.k8s_info:
|
|
|
|
+ kubeconfig: "{{ kubeadmin_config }}"
|
|
validate_certs: no
|
|
validate_certs: no
|
|
api_version: operators.coreos.com/v1alpha1
|
|
api_version: operators.coreos.com/v1alpha1
|
|
- kind: clusterserviceversion
|
|
|
|
- namespace: "{{ op.op_nsp }}"
|
|
|
|
- name: "{{ op.desired_csv }}"
|
|
|
|
- register: new_csv
|
|
|
|
|
|
+ kind: installplan
|
|
|
|
+ namespace: "{{ role.namespace }}"
|
|
|
|
+ register: installplan
|
|
until:
|
|
until:
|
|
- - new_csv.resources is defined
|
|
|
|
- - (new_csv.resources | length) > 0
|
|
|
|
- - new_csv.resources[0].status is defined
|
|
|
|
- - new_csv.resources[0].status.phase == "Succeeded"
|
|
|
|
- retries: 30
|
|
|
|
|
|
+ - installplan.resources is defined
|
|
|
|
+ - (installplan.resources | length) > 0
|
|
|
|
+ - >
|
|
|
|
+ (installplan.resources |
|
|
|
|
+ selectattr('metadata.ownerReferences.0.name', 'in', (role.subscription | default(role.package))) |
|
|
|
|
+ length) > (sub_ip_pre | length)
|
|
|
|
+ retries: 12
|
|
delay: 10
|
|
delay: 10
|
|
|
|
|
|
- # TODO: Finish this at some point.
|
|
|
|
- #- name: Finally, wait for the pod
|
|
|
|
- # k8s_info:
|
|
|
|
- # kubeconfig: tmp/kubeconfig-ocp4
|
|
|
|
- # validate_certs: no
|
|
|
|
- # api_version: v1
|
|
|
|
- # kind: pod
|
|
|
|
- # namespace: rhsso
|
|
|
|
- # label_selectors:
|
|
|
|
- # - name = rhsso-operator
|
|
|
|
- # register: sso_pod
|
|
|
|
- # until:
|
|
|
|
- # - sso_pod.resources is defined
|
|
|
|
- # - (sso_pod.resources | length) > 0
|
|
|
|
- # - sso_pod.resources[0].status is defined
|
|
|
|
- # - sso_pod.resources[0].status.phase == "Running"
|
|
|
|
- # retries: 30
|
|
|
|
- # delay: 10
|
|
|
|
-
|
|
|
|
- - debug:
|
|
|
|
- msg: Operator {{ op.op_pkg }} deployed in namespace {{ op.op_nsp }} with CSV {{ op.desired_csv }}!
|
|
|
|
-
|
|
|
|
- when: found_csv is not defined or (found_csv | length) == 0
|
|
|
|
|
|
+ - name: Remember the installPlan(s) found after the subscription has been modified
|
|
|
|
+ ansible.builtin.set_fact:
|
|
|
|
+ sub_ip_post: "{{ installplan.resources | selectattr('metadata.ownerReferences.0.name', 'in', (role.subscription | default(role.package))) | sort(attribute='metadata.creationTimestamp', reverse=True) }}"
|
|
|
|
+
|
|
|
|
+ - name: Show the installplan(s) found at verbosity 2+
|
|
|
|
+ debug:
|
|
|
|
+ var: sub_ip_post
|
|
|
|
+ verbosity: 2
|
|
|
|
+
|
|
|
|
+ - name: Remember the latest installPlan name as a new fact
|
|
|
|
+ ansible.builtin.set_fact:
|
|
|
|
+ latest_install_plan: "{{ sub_ip_post[0].metadata.name }}"
|
|
|
|
+
|
|
|
|
+ - name: Announce the installPlan name
|
|
|
|
+ ansible.builtin.debug:
|
|
|
|
+ msg: Found installPlan {{ latest_install_plan }}.
|
|
|
|
+
|
|
|
|
+ - name: Approve the installPlan if the approval was Manual
|
|
|
|
+ kubernetes.core.k8s_json_patch:
|
|
|
|
+ kubeconfig: "{{ kubeadmin_config }}"
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: operators.coreos.com/v1alpha1
|
|
|
|
+ kind: installplan
|
|
|
|
+ namespace: "{{ role.namespace }}"
|
|
|
|
+ name: "{{ latest_install_plan }}"
|
|
|
|
+ patch:
|
|
|
|
+ - op: replace
|
|
|
|
+ path: /spec/approved
|
|
|
|
+ value: true
|
|
|
|
+ when: role.approval == "Manual"
|
|
|
|
+
|
|
|
|
+ when: subscription.changed
|
|
|
|
+
|
|
|
|
+- name: Wait for CSV to show up and complete
|
|
|
|
+ kubernetes.core.k8s_info:
|
|
|
|
+ kubeconfig: "{{ kubeadmin_config }}"
|
|
|
|
+ validate_certs: no
|
|
|
|
+ api_version: operators.coreos.com/v1alpha1
|
|
|
|
+ kind: clusterserviceversion
|
|
|
|
+ namespace: "{{ role.namespace }}"
|
|
|
|
+ name: "{{ role.desired_csv }}"
|
|
|
|
+ register: new_csv
|
|
|
|
+ until:
|
|
|
|
+ - new_csv.resources is defined
|
|
|
|
+ - (new_csv.resources | length) > 0
|
|
|
|
+ - new_csv.resources[0].status is defined
|
|
|
|
+ - new_csv.resources[0].status.phase == "Succeeded"
|
|
|
|
+ retries: 30
|
|
|
|
+ delay: 10
|
|
...
|
|
...
|