Grega Bremec 4 miesięcy temu
commit
a1f4bd3c56

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+.DS_Store
+.*.sw?
+*.vim
+tmp*
+target
+.vscode
+*.log
+*.code-workspace

+ 31 - 0
p0f/operators/COPYING

@@ -0,0 +1,31 @@
+This software is being published under terms and conditions set forth
+by the BSD License.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of P0F.NET nor the names of contributors may
+      be used to endorse or promote products derived from this
+      software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+

+ 3 - 0
p0f/operators/README.md

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

+ 68 - 0
p0f/operators/galaxy.yml

@@ -0,0 +1,68 @@
+### REQUIRED
+# The namespace of the collection. This can be a company/brand/organization or product namespace under which all
+# content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with
+# underscores or numbers and cannot contain consecutive underscores
+namespace: p0f
+
+# The name of the collection. Has the same character restrictions as 'namespace'
+name: operators
+
+# The version of the collection. Must be compatible with semantic versioning
+version: 1.0.0
+
+# The path to the Markdown (.md) readme file. This path is relative to the root of the collection
+readme: README.md
+
+# A list of the collection's content authors. Can be just the name or in the format 'Full Name <email> (url)
+# @nicks:irc/im.site#channel'
+authors:
+- Grega Bremec <gregab@p0f.net>
+
+
+### OPTIONAL but strongly recommended
+# A short summary description of the collection
+description: Various roles to aid in management of OLM-controlled operators (primarily on OpenShift).
+
+# The path to the license file for the collection. This path is relative to the root of the collection. This key is
+# mutually exclusive with 'license'
+license_file: COPYING
+
+# A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character
+# requirements as 'namespace' and 'name'
+tags: ["openshift","operators"]
+
+# Collections that this collection requires to be installed for it to be usable. The key of the dict is the
+# collection label 'namespace.name'. The value is a version range
+# L(specifiers,https://python-semanticversion.readthedocs.io/en/latest/#requirement-specification). Multiple version
+# range specifiers can be set and are separated by ','
+dependencies:
+  ansible.posix: >=1.5.4
+  community.general: >=7.5.0
+  kubernetes.core: >=3.0.1
+  redhat.openshift: >=3.0.1
+
+# The URL of the originating SCM repository
+repository: https://git.p0f.net/ansible/operators.git
+
+# The URL to any online docs
+#documentation: http://docs.example.com
+
+# The URL to the homepage of the collection/project
+#homepage: http://example.com
+
+# The URL to the collection issue tracker
+issues: https://git.p0f.net/ansible/operators/issues
+
+# A list of file glob-like patterns used to filter any files or directories that should not be included in the build
+# artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This
+# uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry',
+# and '.git' are always filtered. Mutually exclusive with 'manifest'
+build_ignore: []
+
+# A dict controlling use of manifest directives used in building the collection artifact. The key 'directives' is a
+# list of MANIFEST.in style
+# L(directives,https://packaging.python.org/en/latest/guides/using-manifest-in/#manifest-in-commands). The key
+# 'omit_default_directives' is a boolean that controls whether the default directives are used. Mutually exclusive
+# with 'build_ignore'
+# manifest: null
+

+ 52 - 0
p0f/operators/meta/runtime.yml

@@ -0,0 +1,52 @@
+---
+# Collections must specify a minimum required ansible version to upload
+# to galaxy
+# requires_ansible: '>=2.9.10'
+
+# Content that Ansible needs to load from another location or that has
+# been deprecated/removed
+# plugin_routing:
+#   action:
+#     redirected_plugin_name:
+#       redirect: ns.col.new_location
+#     deprecated_plugin_name:
+#       deprecation:
+#         removal_version: "4.0.0"
+#         warning_text: |
+#           See the porting guide on how to update your playbook to
+#           use ns.col.another_plugin instead.
+#     removed_plugin_name:
+#       tombstone:
+#         removal_version: "2.0.0"
+#         warning_text: |
+#           See the porting guide on how to update your playbook to
+#           use ns.col.another_plugin instead.
+#   become:
+#   cache:
+#   callback:
+#   cliconf:
+#   connection:
+#   doc_fragments:
+#   filter:
+#   httpapi:
+#   inventory:
+#   lookup:
+#   module_utils:
+#   modules:
+#   netconf:
+#   shell:
+#   strategy:
+#   terminal:
+#   test:
+#   vars:
+
+# Python import statements that Ansible needs to load from another location
+# import_redirection:
+#   ansible_collections.ns.col.plugins.module_utils.old_location:
+#     redirect: ansible_collections.ns.col.plugins.module_utils.new_location
+
+# Groups of actions/modules that take a common set of options
+# action_groups:
+#   group_name:
+#     - module1
+#     - module2

+ 221 - 0
p0f/operators/roles/deploy-operators/tasks/main.yml

@@ -0,0 +1,221 @@
+---
+# Ensures all the operator artifacts are created and waits for CSV to succeed.
+#
+# The following variables must exist:
+#
+#   op:
+#     desired_csv
+#     op_nsp
+#     op_cat
+#     op_pkg
+#     op_chn
+#     op_mod
+#
+# 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
+    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
+
+- 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') }}"
+  when:
+    - found_csv is defined
+    - (found_csv | length) > 0
+
+- debug:
+    msg: Operator {{ op.op_pkg }} is already installed in {{ csv_ns }} as {{ csv_name }} (desired_csv == {{ op.desired_csv }}).
+  when:
+    - csv_ns is defined
+    - csv_name is defined
+
+- name: Only proceed here if the CSV was not found anywhere yet
+  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
+        validate_certs: no
+        api_version: operators.coreos.com/v1alpha1
+        kind: clusterserviceversion
+        namespace: "{{ op.op_nsp }}"
+        name: "{{ op.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
+
+    - 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
+...

+ 7 - 0
p0f/operators/roles/fix-operators/files/pull-secret.yml

@@ -0,0 +1,7 @@
+apiVersion: v1
+kind: Secret
+metadata:
+  name: pull-secret
+  namespace: openshift-config
+stringData:
+  .dockerconfigjson: '{"auths":{"registry.somewhere.com":{"auth":"AUTHSTRING","email":"YOUREMAIL@SOMEWHERE.COM"}}}'

+ 128 - 0
p0f/operators/roles/fix-operators/tasks/main.yml

@@ -0,0 +1,128 @@
+---
+# Fixes the openshift-marketplace catalogs by recreating them from original images.
+#
+# REQUIRED:
+#
+#   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:
+#
+# 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:
+#
+#   vrfy_cat     catalog source
+#   vrfy_pkg     operator package name
+#   vrfy_csv     csv we look for
+#
+# This is necessary immediately after lab create.
+- name: Wait for the marketplace-operator to be up
+  k8s_info:
+    kubeconfig: tmp/kubeconfig-ocp4
+    validate_certs: no
+    api_version: v1
+    kind: pod
+    namespace: openshift-marketplace
+    label_selectors:
+      - name=marketplace-operator
+  register: mktplc_pod
+  until:
+    - (mktplc_pod.resources | length) == 1
+    - mktplc_pod.resources[0].status.containerStatuses[0].ready
+  retries: 30
+  delay: 10
+
+- name: Make sure the course catalog is not there
+  k8s:
+    kubeconfig: tmp/kubeconfig-ocp4
+    validate_certs: no
+    api_version: operators.coreos.com/v1alpha1
+    kind: catalogsource
+    namespace: openshift-marketplace
+    name: "{{ item }}"
+    state: absent
+  loop: "{{ removed_sources }}"
+
+- name: Make sure the pull secret will do for online sources
+  k8s:
+    kubeconfig: tmp/kubeconfig-ocp4
+    validate_certs: no
+    api_version: v1
+    kind: secret
+    namespace: openshift-config
+    name: pull-secret
+    state: present
+    definition: "{{ lookup('file', 'files/pull-secret.yml') | from_yaml }}"
+
+- name: Ensure the standard catalog sources are there
+  k8s:
+    kubeconfig: tmp/kubeconfig-ocp4
+    validate_certs: no
+    api_version: operators.coreos.com/v1alpha1
+    kind: catalogsource
+    namespace: openshift-marketplace
+    name: "{{ item.name }}"
+    state: present
+    definition:
+      spec:
+        displayName: "{{ item.displ }}"
+        image: "{{ item.image }}"
+        publisher: "Red Hat"
+        sourceType: "grpc"
+  loop: "{{ catalog_sources }}"
+  loop_control:
+    label: "{{ item.displ }}"
+
+- name: Wait for the catalogsources to be ready.
+  k8s_info:
+    kubeconfig: tmp/kubeconfig-ocp4
+    validate_certs: no
+    api_version: operators.coreos.com/v1alpha1
+    kind: catalogsource
+    namespace: openshift-marketplace
+    name: "{{ item.name }}"
+  register: cat_stat
+  until:
+    - (cat_stat.resources | length) == 1
+    - cat_stat.resources[0].status is defined
+    - cat_stat.resources[0].status.connectionState.lastObservedState == "READY"
+  retries: 30
+  delay: 10
+  loop: "{{ catalog_sources }}"
+  loop_control:
+    label: "{{ item.displ }}"
+
+- name: Verify correct deployment
+  block:
+    - name: Wait for the operator packagemanifest to appear.
+      k8s_info:
+        kubeconfig: tmp/kubeconfig-ocp4
+        validate_certs: no
+        api_version: packages.operators.coreos.com/v1
+        kind: packagemanifest
+        namespace: openshift-marketplace
+        name: "{{ vrfy_pkg }}"
+      register: vrfy_mft
+      until:
+        - (vrfy_mft.resources | length) == 1
+        - vrfy_mft.resources[0].status.catalogSource == vrfy_cat
+        - vrfy_mft.resources[0].status.packageName == vrfy_pkg
+      retries: 60
+      delay: 10
+
+    - assert:
+        that:
+          - vrfy_mft.resources is defined
+          - (vrfy_mft.resources | length) > 0
+          - vrfy_mft.resources[0].status.catalogSource == vrfy_cat
+          - 'vrfy_csv in (vrfy_mft.resources[0] | community.general.json_query("status.channels[*].currentCSV") | list)'
+        fail_msg: "ERROR: {{ vrfy_pkg }} package manifest not deployed correctly."
+        success_msg: "OK: {{ vrfy_pkg }} package manifest configured correctly."
+
+  when:
+    - vrfy_cat is defined
+    - vrfy_pkg is defined
+    - vrfy_csv is defined
+...

+ 17 - 0
p0f/operators/roles/fix-operators/vars/main.yml

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

+ 71 - 0
p0f/operators/roles/remove-operators/tasks/main.yml

@@ -0,0 +1,71 @@
+---
+# Ensures all the operator artifacts are created and waits for CSV to succeed.
+#
+# The following variables must exist:
+#
+#   removed_operators:    a list of below dictionaries
+#     - 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
+#
+# This role must then be applied as:
+#
+#   - include_role:
+#       name: remove-operators
+#     vars:
+#       - role: "{{ item }}"
+#     loop: "{{ removed_operators }}"
+#
+# 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
+  loop: "{{ role.pre_cleanup }}"
+  register: removed
+
+- name: Remove the subscription
+  k8s:
+    kubeconfig: tmp/kubeconfig-ocp4
+    validate_certs: no
+    api_version: operators.coreos.com/v1alpha1
+    kind: subscription
+    name: "{{ role.sub_name }}"
+    namespace: "{{ role.sub_nspc }}"
+    state: absent
+  ignore_errors: yes
+
+- name: Do post-cleanup
+  k8s:
+    kubeconfig: tmp/kubeconfig-ocp4
+    validate_certs: no
+    api_version: apiextensions.k8s.io/v1
+    kind: customresourcedefinition
+    name: "{{ item }}"
+    state: absent
+  ignore_errors: yes
+  loop: "{{ role.add_cleanup }}"
+
+- name: Remove the CSV as well, if so required
+  k8s:
+    kubeconfig: tmp/kubeconfig-ocp4
+    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
+...