Sfoglia il codice sorgente

rename role deploy-operator to plural, implement operatorgroup checks, installplan checks, auto and manual approval

Grega Bremec 1 mese fa
parent
commit
22aeef5d9f

+ 9 - 0
playbooks/inventory.yml

@@ -25,6 +25,15 @@ all:
         namespace: metallb-system
         desired_csv: metallb-operator.v4.16.0-202507221936
         og_namespaces: []
+        approval: Manual
+      - catalog: redhat-operators
+        package: rhbk-operator
+        channel: stable-v26.2
+        namespace: keycloak
+        desired_csv: rhbk-operator.v26.2.7-opr.1
+        og_namespaces:
+          - keycloak
+        approval: Manual
 
     # Some cleanup here.
     removed_operators:

+ 1 - 1
playbooks/pre-flight.yml

@@ -37,7 +37,7 @@
         - fix
     # Re-apply any operators that have had their catalog sources changed.
     - include_role:
-        name: deploy-operator
+        name: deploy-operators
         apply:
           tags:
             - prep

+ 0 - 115
playbooks/roles/deploy-operator/tasks/main.yml

@@ -1,115 +0,0 @@
----
-# Ensures all the operator artifacts are created and waits for CSV to succeed.
-#
-# The following variables must exist:
-#
-#  added_operators:   (list)
-#    - catalog:       the catalog of the manifest
-#      package:       the name of the packagemanifest
-#      subscription:  the name of the 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 (XXX not currently used XXX)
-#
-# 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.
-#
-# NOTE: Do NOT test by checking for presence of API resources - they do not always get cleaned up.
-#
-- name: Make sure the namespace is there
-  k8s:
-    kubeconfig: tmp/kubeconfig-ocp4
-    validate_certs: no
-    api_version: v1
-    kind: namespace
-    name: "{{ role.namespace }}"
-
-# TODO: Finish this at some point.
-# TODO: Just apply from role.og_namespaces - finding an OG or just applying a default name.
-#- name: Make sure the namespace has a properly configured OperatorGroup
-#  k8s_info:
-#    kubeconfig: tmp/kubeconfig-ocp4
-#    validate_certs: no
-#    api_version: operators.coreos.com/v1
-#    kind: operatorgroup
-#    namespace: "{{ op_nsp }}"
-#  register: found_opgrp
-
-- 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: "{{ 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: 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
-    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
-
-# 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
-...

+ 189 - 0
playbooks/roles/deploy-operators/tasks/main.yml

@@ -0,0 +1,189 @@
+---
+# Ensures all the operator artifacts are created and waits for CSV to succeed.
+#
+# The following variables must exist:
+#
+#  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 (XXX not currently used XXX)
+#      approval:      Automatic (default) or Manual
+#
+# 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.
+#
+# 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: Make sure the namespace is there
+  kubernetes.core.k8s:
+    kubeconfig: tmp/kubeconfig-ocp4
+    validate_certs: no
+    api_version: v1
+    kind: namespace
+    name: "{{ role.namespace }}"
+
+- name: Verify if the namespace has an OperatorGroup already
+  kubernetes.core.k8s_info:
+    kubeconfig: tmp/kubeconfig-ocp4
+    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:
+    - found_opgrp.resources is defined
+    - found_opgrp.resources[0] is defined
+    - found_opgrp.resources[0].spec is defined
+
+- name: If not there yet, create an OperatorGroup
+  kubernetes.core.k8s:
+    kubeconfig: tmp/kubeconfig-ocp4
+    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:
+    - >
+      found_opgrp.resources is not defined or
+      found_opgrp.resources[0] is not defined
+
+- name: Check if installPlans exist already before fondling around with subscriptions
+  kubernetes.core.k8s_info:
+    kubeconfig: tmp/kubeconfig-ocp4
+    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: tmp/kubeconfig-ocp4
+    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:
+    - name: Wait for installPlan to show up
+      kubernetes.core.k8s_info:
+        kubeconfig: tmp/kubeconfig-ocp4
+        validate_certs: no
+        api_version: operators.coreos.com/v1alpha1
+        kind: installplan
+        namespace: "{{ role.namespace }}"
+      register: installplan
+      until:
+        - 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
+
+    - 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: tmp/kubeconfig-ocp4
+        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: tmp/kubeconfig-ocp4
+    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
+...