Browse Source

add latest changes from various projects

Grega Bremec 1 month ago
parent
commit
f68cddb159

+ 1 - 0
p0f/operators/README.md

@@ -1,3 +1,4 @@
 # Ansible Collection - p0f.operators
 
 Documentation for the collection.
+

+ 1 - 1
p0f/operators/meta/runtime.yml

@@ -1,7 +1,7 @@
 ---
 # Collections must specify a minimum required ansible version to upload
 # to galaxy
-# requires_ansible: '>=2.9.10'
+requires_ansible: '>=2.18.0'
 
 # Content that Ansible needs to load from another location or that has
 # been deprecated/removed

+ 4 - 0
p0f/operators/roles/deploy-operators/defaults/main.yml

@@ -0,0 +1,4 @@
+---
+# Variables that are usually overridden.
+kubeadmin_config: "tmp/kubeconfig-ocp4"
+...

+ 181 - 202
p0f/operators/roles/deploy-operators/tasks/main.yml

@@ -3,219 +3,198 @@
 #
 # 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
-    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:
-    - 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:
-    - 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:
-
-    # 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
         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:
-        - 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
 
-    # 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
 ...

+ 4 - 0
p0f/operators/roles/fix-operators/defaults/main.yml

@@ -0,0 +1,4 @@
+---
+# Variables that are usually overridden.
+kubeadmin_config: "tmp/kubeconfig-ocp4"
+...

+ 61 - 19
p0f/operators/roles/fix-operators/tasks/main.yml

@@ -3,18 +3,19 @@
 #
 # IMPORTANT: Wherever this role is applied, there must be a files/pull-secret.yml!
 #
-# TODO: Check if pull secret can be resolved and quit?
-#
-# TODO: Fix existing subscriptions, patching their catalogs!
-#
 # REQUIRED:
 #
+#   ocp_maj             OpenShift version (x.y; re catalogsource image)
+#
+#   registry_server     the server catalogs come from (vars/main.yml, used in
+#                         catalog_sources, but in pull secret check, too)
 #   removed_sources     the catalog sources we remove (vars/main.yml)
 #   catalog_sources     the catalog sources we recreate (vars/main.yml)
-#   ocp_maj             OpenShift version (x.y; re catalogsource image)
 #
 # OPTIONAL:
 #
+#   kubeadmin_config    kubeadmin (or other admin) credentials (tmp/kubeconfig-ocp4)
+#
 # These would usually come from inventory, and should point to a single
 # manifest and its CSV that we can use to verify catalog sources were created
 # and populated successfully:
@@ -23,10 +24,13 @@
 #   vrfy_pkg     operator package name
 #   vrfy_csv     csv we look for
 #
+# NOTE: Fixing existing subscriptions, patching their catalogs, changing CSVs,
+#       etc., is performed by deploy-operators role.
+#
 # This is necessary immediately after lab create.
 - name: Wait for the marketplace-operator to be up
-  k8s_info:
-    kubeconfig: tmp/kubeconfig-ocp4
+  kubernetes.core.k8s_info:
+    kubeconfig: "{{ kubeadmin_config }}"
     validate_certs: no
     api_version: v1
     kind: pod
@@ -41,8 +45,8 @@
   delay: 10
 
 - name: Make sure the course catalog is not there
-  k8s:
-    kubeconfig: tmp/kubeconfig-ocp4
+  kubernetes.core.k8s:
+    kubeconfig: "{{ kubeadmin_config }}"
     validate_certs: no
     api_version: operators.coreos.com/v1alpha1
     kind: catalogsource
@@ -51,9 +55,25 @@
     state: absent
   loop: "{{ removed_sources }}"
 
-- name: Make sure the pull secret will do for online sources
-  k8s:
-    kubeconfig: tmp/kubeconfig-ocp4
+- name: Extract the pull-secret in openshift-config namespace
+  kubernetes.core.k8s_info:
+    kubeconfig: "{{ kubeadmin_config }}"
+    validate_certs: no
+    api_version: v1
+    kind: secret
+    namespace: openshift-config
+    name: pull-secret
+  register: existing_pull_secret
+
+- name: Try to extract the credential for registry_server
+  ansible.builtin.set_fact:
+    regsvr_cred: |
+      {{ existing_pull_secret.resources[0].data['.dockerconfigjson'] | b64decode | from_json |
+          community.general.json_query('auths."' + registry_server + '".auth') }}
+
+- name: Make sure the pull secret will do for online sources if the existing one does not suffice
+  kubernetes.core.k8s:
+    kubeconfig: "{{ kubeadmin_config }}"
     validate_certs: no
     api_version: v1
     kind: secret
@@ -61,10 +81,32 @@
     name: pull-secret
     state: present
     definition: "{{ lookup('file', 'files/pull-secret.yml') | from_yaml }}"
+  when: regsvr_cred is not defined or regsvr_cred == None or regsvr_cred == ''
+
+- name: Try to obtain cluster version if not set by ocp_maj
+  block:
+    - name: Read clusterversion/version
+      kubernetes.core.k8s_info:
+        kubeconfig: "{{ kubeadmin_config }}"
+        validate_certs: no
+        api_version: config.openshift.io/v1
+        kind: clusterversion
+        name: version
+      register: clusterversion
+
+    - name: Store it as a fact
+      ansible.builtin.set_fact:
+        ocp_z: "{{ clusterversion.resources[0].status.desired.version }}"
+
+    - name: Store the major version as well
+      ansible.builtin.set_fact:
+        ocp_maj: "{{ ocp_z | ansible.builtin.regex_replace('\\.\\d+$', '') }}"
+
+  when: ocp_maj is not defined
 
 - name: Ensure the standard catalog sources are there
-  k8s:
-    kubeconfig: tmp/kubeconfig-ocp4
+  kubernetes.core.k8s:
+    kubeconfig: "{{ kubeadmin_config }}"
     validate_certs: no
     api_version: operators.coreos.com/v1alpha1
     kind: catalogsource
@@ -82,8 +124,8 @@
     label: "{{ item.displ }}"
 
 - name: Wait for the catalogsources to be ready.
-  k8s_info:
-    kubeconfig: tmp/kubeconfig-ocp4
+  kubernetes.core.k8s_info:
+    kubeconfig: "{{ kubeadmin_config }}"
     validate_certs: no
     api_version: operators.coreos.com/v1alpha1
     kind: catalogsource
@@ -103,8 +145,8 @@
 - name: Verify correct deployment
   block:
     - name: Wait for the operator packagemanifest to appear.
-      k8s_info:
-        kubeconfig: tmp/kubeconfig-ocp4
+      kubernetes.core.k8s_info:
+        kubeconfig: "{{ kubeadmin_config }}"
         validate_certs: no
         api_version: packages.operators.coreos.com/v1
         kind: packagemanifest
@@ -118,7 +160,7 @@
       retries: 60
       delay: 10
 
-    - assert:
+    - ansible.builtin.assert:
         that:
           - vrfy_mft.resources is defined
           - (vrfy_mft.resources | length) > 0

+ 5 - 3
p0f/operators/roles/fix-operators/vars/main.yml

@@ -3,15 +3,17 @@ removed_sources:
   - do280-catalog
   - do280-catalog-cs
   - do288-catalog-cs
+  - do370-catalog-cs
   - do380-catalog-cs
+registry_server: registry.redhat.io
 catalog_sources:
   - name: redhat-operators
     displ: Red Hat Operators
-    image: registry.redhat.io/redhat/redhat-operator-index:v{{ ocp_maj }}
+    image: "{{ registry_server }}/redhat/redhat-operator-index:v{{ ocp_maj }}"
   - name: certified-operators
     displ: Certified Operators
-    image: registry.redhat.io/redhat/certified-operator-index:v{{ ocp_maj }}
+    image: "{{ registry_server }}/redhat/certified-operator-index:v{{ ocp_maj }}"
   - name: community-operators
     displ: Community Operators
-    image: registry.redhat.io/redhat/community-operator-index:v{{ ocp_maj }}
+    image: "{{ registry_server }}/redhat/community-operator-index:v{{ ocp_maj }}"
 ...

+ 4 - 0
p0f/operators/roles/remove-operators/defaults/main.yml

@@ -0,0 +1,4 @@
+---
+# Variables that are usually overridden.
+kubeadmin_config: "tmp/kubeconfig-ocp4"
+...

+ 46 - 29
p0f/operators/roles/remove-operators/tasks/main.yml

@@ -7,37 +7,41 @@
 #     - sub_nspc          subscription namespace
 #       sub_name          subscription name
 #       csv_name          CSV name to check for
-#       pre_cleanup       pre-cleanup tasks, a list rtypes to remove (ALL)
-#         - apiv
-#           kind
-#       add_cleanup       additional CRDs to remove post-uninstall, a list
+#       pre_cleanup       pre-cleanup tasks, a list of rtypes to remove (ALL)
+#         - apiv            api version
+#           kind            resource kind
+#           nspc            namespace (required for namespaced resources)
+#           wait_for_gone   whether to wait for the resource to disappear
+#           wait_for_sec    how long (in seconds) to wait for gone
+#       add_cleanup       additional CRDs to remove post-uninstall, a list of
+#         - name            crd name, OR
+#           label           crd label, in case both are defined, both are used
 #
 # This role must then be applied as:
 #
 #   - include_role:
 #       name: remove-operators
-#     vars:
-#       - role: "{{ item }}"
 #     loop: "{{ removed_operators }}"
+#     loop_control:
+#       loop_var: role
 #
 # What this means is that each item of removed_operators is expected to be
 # placed in the "role" variable prior to iterating over this role.
 #
-- name: Remove any of the resources found
-  k8s:
-    kubeconfig: tmp/kubeconfig-ocp4
-    validate_certs: no
-    api_version: "{{ item.apiv }}"
-    kind: "{{ item.kind }}"
-    delete_all: true
-    state: absent
-  ignore_errors: yes
+# OPTIONAL:
+#
+#   kubeadmin_config    kubeadmin (or other admin) credentials (tmp/kubeconfig-ocp4)
+#
+# Must include tasks because you can't loop over a block.
+- name: Remove any of the resources found from pre_cleanup
+  ansible.builtin.include_tasks: tasks/pre-cleanup.yml
   loop: "{{ role.pre_cleanup }}"
-  register: removed
+  loop_control:
+    label: "{{ item.kind }}.{{ item.apiv }}"
 
 - name: Remove the subscription
-  k8s:
-    kubeconfig: tmp/kubeconfig-ocp4
+  kubernetes.core.k8s:
+    kubeconfig: "{{ kubeadmin_config }}"
     validate_certs: no
     api_version: operators.coreos.com/v1alpha1
     kind: subscription
@@ -46,26 +50,39 @@
     state: absent
   ignore_errors: yes
 
+- name: Remove the CSV as well, if so required
+  kubernetes.core.k8s:
+    kubeconfig: "{{ kubeadmin_config }}"
+    validate_certs: no
+    api_version: operators.coreos.com/v1alpha1
+    kind: clusterserviceversion
+    name: "{{ role.csv_name }}"
+    namespace: "{{ role.sub_nspc }}"
+    state: absent
+  ignore_errors: yes
+  when: role.csv_kill
+
 - name: Do post-cleanup
-  k8s:
-    kubeconfig: tmp/kubeconfig-ocp4
+  kubernetes.core.k8s:
+    kubeconfig: "{{ kubeadmin_config }}"
     validate_certs: no
     api_version: apiextensions.k8s.io/v1
     kind: customresourcedefinition
-    name: "{{ item }}"
+    name: "{{ item.name | default(omit) }}"
+    label_selectors:
+      - "{{ item.label | default(omit) }}"
     state: absent
   ignore_errors: yes
   loop: "{{ role.add_cleanup }}"
 
-- name: Remove the CSV as well, if so required
-  k8s:
-    kubeconfig: tmp/kubeconfig-ocp4
+- name: Lastly, remove the namespace, if instructed
+  kubernetes.core.k8s:
+    kubeconfig: "{{ kubeadmin_config }}"
     validate_certs: no
-    api_version: operators.coreos.com/v1alpha1
-    kind: clusterserviceversion
-    name: "{{ role.csv_name }}"
-    namespace: "{{ role.sub_nspc }}"
+    api_version: v1
+    kind: namespace
+    name: "{{ role.sub_nspc }}"
     state: absent
   ignore_errors: yes
-  when: role.csv_kill
+  when: role.nsp_kill
 ...

+ 47 - 0
p0f/operators/roles/remove-operators/tasks/pre-cleanup.yml

@@ -0,0 +1,47 @@
+---
+- name: Tell what is being done.
+  ansible.builtin.pause:
+    prompt: |
+      ********************************************************************
+      
+      Removing all instances of {{ item.kind }}.{{ item.apiv }}
+      
+      ********************************************************************
+    seconds: 0
+
+- name: Remove the resources
+  kubernetes.core.k8s:
+    kubeconfig: "{{ kubeadmin_config }}"
+    validate_certs: no
+    api_version: "{{ item.apiv }}"
+    kind: "{{ item.kind }}"
+    namespace: "{{ item.nspc | default(omit) }}"
+    delete_all: true
+    state: absent
+  ignore_errors: yes
+  register: removed_rsrc
+
+- ansible.builtin.debug:
+    verbosity: 2
+    var: removed_rsrc
+
+- name: Wait for the resource(s) to be gone if so requested
+  kubernetes.core.k8s_info:
+    kubeconfig: "{{ kubeadmin_config }}"
+    validate_certs: no
+    api_version: "{{ item.apiv }}"
+    kind: "{{ item.kind }}"
+    namespace: "{{ item.nspc | default(omit) }}"
+  ignore_errors: yes
+  register: remaining_rsrc
+  until: (remaining_rsrc.resources | length) == 0
+  retries: "{{ (item.wait_for_sec | default(6)) / 5 }}"
+  delay: 5
+  when:
+    - item.wait_for_gone is defined
+    - item.wait_for_gone
+
+- ansible.builtin.debug:
+    verbosity: 2
+    var: remaining_rsrc
+...