diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index f15068a..ae4a142 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -26,4 +26,4 @@ jobs: - uses: ansible-network/github_actions/.github/actions/checkout_dependency@main - name: run-ansible-lint - uses: ansible/ansible-lint@v24.7.0 + uses: ansible/ansible-lint@v25.1.2 diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 57ff6e0..b64296b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,14 @@ OKD Collection Release Notes .. contents:: Topics +v4.0.2 +====== + +Release Summary +--------------- + +This patch updates the k8s dependency version to the 5.x range and modifies tests to handle the manual creation of service account tokens. + v4.0.1 ====== diff --git a/Makefile b/Makefile index 9c9574e..7b61c7a 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: molecule # Also needs to be updated in galaxy.yml -VERSION = 4.0.1 +VERSION = 6.0.0-dev0 SANITY_TEST_ARGS ?= --docker --color UNITS_TEST_ARGS ?= --docker --color diff --git a/README.md b/README.md index 2a23077..a5812b9 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ You can also include it in a `requirements.yml` file and install it via `ansible --- collections: - name: community.okd - version: 4.0.1 + version: 4.0.2 ``` ### Installing the Kubernetes Python Library diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 6174adc..ed607bf 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -250,3 +250,10 @@ releases: fragments: - 242-fix-failed-token-deletion.yml release_date: '2024-12-03' + 4.0.2: + changes: + release_summary: This patch updates the k8s dependency version to the 5.x range + and modifies tests to handle the manual creation of service account tokens. + fragments: + - 4.0.2.yml + release_date: '2025-05-28' diff --git a/changelogs/fragments/242-fix-failed-token-deletion.yml b/changelogs/fragments/242-fix-failed-token-deletion.yml deleted file mode 100644 index a0b770d..0000000 --- a/changelogs/fragments/242-fix-failed-token-deletion.yml +++ /dev/null @@ -1,2 +0,0 @@ -bugfixes: - - openshift_auth - fix issue where openshift_auth module sometimes does not delete the auth token. Based on stale PR (https://github.com/openshift/community.okd/pull/194). \ No newline at end of file diff --git a/changelogs/fragments/ansible-lint-update.yml b/changelogs/fragments/ansible-lint-update.yml new file mode 100644 index 0000000..f195691 --- /dev/null +++ b/changelogs/fragments/ansible-lint-update.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - Bump version of ansible-lint to 25.1.2 (https://github.com/openshift/community.okd/pull/255). diff --git a/ci/downstream.sh b/ci/downstream.sh index 1fad929..6974e5b 100755 --- a/ci/downstream.sh +++ b/ci/downstream.sh @@ -9,7 +9,7 @@ # - All functions are prefixed with f_ so it's obvious where they come # from when in use throughout the script -DOWNSTREAM_VERSION="4.0.1" +DOWNSTREAM_VERSION="6.0.0-dev0" KEEP_DOWNSTREAM_TMPDIR="${KEEP_DOWNSTREAM_TMPDIR:-''}" INSTALL_DOWNSTREAM_COLLECTION_PATH="${INSTALL_DOWNSTREAM_COLLECTION_PATH:-}" _build_dir="" diff --git a/galaxy.yml b/galaxy.yml index 31082c9..0d8a382 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -5,7 +5,7 @@ authors: - willthames (https://github.com/willthames) - Akasurde (https://github.com/akasurde) dependencies: - kubernetes.core: '>=5.0.0' + kubernetes.core: '>=6.0.0' description: OKD Collection for Ansible. documentation: '' homepage: '' @@ -24,4 +24,4 @@ tags: - okd - cluster # Also needs to be updated in the Makefile -version: 4.0.1 +version: 6.0.0-dev0 diff --git a/meta/runtime.yml b/meta/runtime.yml index 322ebd3..324bb3f 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -19,11 +19,10 @@ plugin_routing: redirect: community.okd.openshift_auth inventory: openshift: - deprecation: + tombstone: removal_version: 5.0.0 warning_text: >- - The openshift inventory plugin has been deprecated and - will be removed in release 5.0.0. + Use kubernetes.core.k8s_info and ansible.builtin.add_host instead. action: k8s: redirect: kubernetes.core.k8s_info diff --git a/molecule/default/files/nginx.env b/molecule/default/files/nginx.env index 939ad0d..e6bcb3a 100644 --- a/molecule/default/files/nginx.env +++ b/molecule/default/files/nginx.env @@ -1,6 +1,7 @@ # Want to make sure comments don't break it export NAME=test123 NAMESPACE=openshift +NGINX_VERSION=1.22-ubi8 diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml index 7ca7e2c..3b56607 100644 --- a/molecule/default/molecule.yml +++ b/molecule/default/molecule.yml @@ -14,12 +14,7 @@ provisioner: log: true options: vvv: True - config_options: - inventory: - enable_plugins: community.okd.openshift inventory: - hosts: - plugin: community.okd.openshift host_vars: localhost: virtualenv: ${MOLECULE_EPHEMERAL_DIRECTORY}/virtualenv diff --git a/molecule/default/tasks/openshift_adm_prune_auth_clusterroles.yml b/molecule/default/tasks/openshift_adm_prune_auth_clusterroles.yml index bac18bf..e46ec40 100644 --- a/molecule/default/tasks/openshift_adm_prune_auth_clusterroles.yml +++ b/molecule/default/tasks/openshift_adm_prune_auth_clusterroles.yml @@ -3,6 +3,7 @@ - set_fact: test_sa: "clusterrole-sa" test_ns: "clusterrole-ns" + test_tn: "clusterrole-tn" - name: Ensure namespace kubernetes.core.k8s: @@ -26,34 +27,27 @@ name: "{{ test_sa }}" namespace: "{{ test_ns }}" - - name: Read Service Account - kubernetes.core.k8s_info: - kind: ServiceAccount - namespace: "{{ test_ns }}" - name: "{{ test_sa }}" - register: result - - - set_fact: - secret_token: "{{ result.resources[0]['secrets'][0]['name'] }}" + - name: Create SA token + kubernetes.core.k8s: + definition: + apiVersion: v1 + kind: Secret + metadata: + name: "{{ test_tn }}" + namespace: "{{ test_ns }}" + annotations: + kubernetes.io/service-account.name: "{{ test_sa }}" + type: kubernetes.io/service-account-token - name: Get secret details kubernetes.core.k8s_info: kind: Secret - namespace: '{{ test_ns }}' - name: '{{ secret_token }}' + namespace: "{{ test_ns }}" + name: "{{ test_tn }}" register: _secret - retries: 10 - delay: 10 - until: - - ("'openshift.io/token-secret.value' in _secret.resources[0]['metadata']['annotations']") or ("'token' in _secret.resources[0]['data']") - - - set_fact: - api_token: "{{ _secret.resources[0]['metadata']['annotations']['openshift.io/token-secret.value'] }}" - when: "'openshift.io/token-secret.value' in _secret.resources[0]['metadata']['annotations']" - set_fact: api_token: "{{ _secret.resources[0]['data']['token'] | b64decode }}" - when: "'token' in _secret.resources[0]['data']" - name: list Node should failed (forbidden user) kubernetes.core.k8s_info: diff --git a/molecule/default/tasks/openshift_adm_prune_auth_roles.yml b/molecule/default/tasks/openshift_adm_prune_auth_roles.yml index 652c423..c9913f1 100644 --- a/molecule/default/tasks/openshift_adm_prune_auth_roles.yml +++ b/molecule/default/tasks/openshift_adm_prune_auth_roles.yml @@ -4,6 +4,7 @@ test_ns: "prune-roles" sa_name: "roles-sa" pod_name: "pod-prune" + tn_name: "roles-sa-token" role_definition: - name: pod-list labels: @@ -50,34 +51,27 @@ name: '{{ sa_name }}' namespace: '{{ test_ns }}' - - name: Read Service Account - kubernetes.core.k8s_info: - kind: ServiceAccount - namespace: '{{ test_ns }}' - name: '{{ sa_name }}' - register: sa_out - - - set_fact: - secret_token: "{{ sa_out.resources[0]['secrets'][0]['name'] }}" + - name: Create SA secret + kubernetes.core.k8s: + definition: + apiVersion: v1 + kind: Secret + metadata: + name: "{{ tn_name }}" + namespace: "{{ test_ns }}" + annotations: + kubernetes.io/service-account.name: "{{ sa_name }}" + type: kubernetes.io/service-account-token - name: Get secret details kubernetes.core.k8s_info: kind: Secret namespace: '{{ test_ns }}' - name: '{{ secret_token }}' + name: '{{ tn_name }}' register: r_secret - retries: 10 - delay: 10 - until: - - ("'openshift.io/token-secret.value' in r_secret.resources[0]['metadata']['annotations']") or ("'token' in r_secret.resources[0]['data']") - - - set_fact: - api_token: "{{ r_secret.resources[0]['metadata']['annotations']['openshift.io/token-secret.value'] }}" - when: "'openshift.io/token-secret.value' in r_secret.resources[0]['metadata']['annotations']" - set_fact: api_token: "{{ r_secret.resources[0]['data']['token'] | b64decode }}" - when: "'token' in r_secret.resources[0]['data']" - name: list resources using service account kubernetes.core.k8s_info: diff --git a/molecule/default/tasks/openshift_process.yml b/molecule/default/tasks/openshift_process.yml index 4341bf2..feb1208 100644 --- a/molecule/default/tasks/openshift_process.yml +++ b/molecule/default/tasks/openshift_process.yml @@ -7,6 +7,7 @@ parameters: NAMESPACE: openshift NAME: test123 + NGINX_VERSION: "{{ nginx_version }}" register: result - name: Create the rendered resources @@ -32,6 +33,7 @@ parameters: NAMESPACE: openshift NAME: test123 + NGINX_VERSION: "{{ nginx_version }}" state: present namespace_target: process-test register: result @@ -44,6 +46,7 @@ NAMESPACE: openshift NAME: test123 MEMORY_LIMIT: 1Gi + NGINX_VERSION: "{{ nginx_version }}" state: present namespace_target: process-test register: result @@ -55,6 +58,7 @@ parameters: NAMESPACE: openshift NAME: test123 + NGINX_VERSION: "{{ nginx_version }}" state: absent namespace_target: process-test register: result diff --git a/molecule/default/verify.yml b/molecule/default/verify.yml index b787062..2b9b008 100644 --- a/molecule/default/verify.yml +++ b/molecule/default/verify.yml @@ -1,9 +1,31 @@ --- -- name: Verify inventory and connection plugins - # This group is created by the openshift_inventory plugin - # It is automatically configured to use the `oc` connection plugin +- name: Create inventory of pods + # We need to manually create the inventory of pods now that the inventory plugin has been removed + gather_facts: false + hosts: localhost + connection: local + tasks: + - name: Get pods + kubernetes.core.k8s_info: + kind: Pod + namespace: testing + register: pods + + - name: Add pods to inventory + ansible.builtin.add_host: + name: "{{ item.metadata.name }}" + groups: + - namespace_testing_pods + ansible_oc_pod: "{{ item.metadata.name }}" + ansible_oc_namespace: "{{ item.metadata.namespace }}" + pod_phase: "{{ item.status.phase }}" + ansible_remote_tmp: /tmp/.ansible + loop: "{{ pods.resources }}" + +- name: Verify connection plugin hosts: namespace_testing_pods gather_facts: no + connection: community.okd.oc vars: file_content: | Hello world @@ -77,6 +99,7 @@ - import_tasks: tasks/openshift_process.yml vars: files_dir: '{{ playbook_dir }}/files' + nginx_version: 1.22-ubi8 always: - name: Delete namespace community.okd.k8s: diff --git a/plugins/inventory/openshift.py b/plugins/inventory/openshift.py deleted file mode 100644 index c9b1f4e..0000000 --- a/plugins/inventory/openshift.py +++ /dev/null @@ -1,252 +0,0 @@ -# Copyright (c) 2018 Ansible Project -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -DOCUMENTATION = """ - name: openshift - author: - - Chris Houseknecht (@chouseknecht) - - short_description: OpenShift inventory source - - description: - - Fetch containers, services and routes for one or more clusters - - Groups by cluster name, namespace, namespace_services, namespace_pods, namespace_routes, and labels - - Uses openshift.(yml|yaml) YAML configuration file to set parameter values. - - deprecated: - removed_in: 5.0.0 - why: | - As discussed in U(https://github.com/ansible-collections/kubernetes.core/issues/31), we decided to - remove the openshift inventory plugin in release 5.0.0. - alternative: "Use M(kubernetes.core.k8s_info) and M(ansible.builtin.add_host) instead." - - options: - plugin: - description: token that ensures this is a source file for the 'openshift' plugin. - required: True - choices: ['openshift', 'community.okd.openshift'] - connections: - description: - - Optional list of cluster connection settings. If no connections are provided, the default - I(~/.kube/config) and active context will be used, and objects will be returned for all namespaces - the active user is authorized to access. - suboptions: - name: - description: - - Optional name to assign to the cluster. If not provided, a name is constructed from the server - and port. - kubeconfig: - description: - - Path to an existing Kubernetes config file. If not provided, and no other connection - options are provided, the Kubernetes client will attempt to load the default - configuration file from I(~/.kube/config). Can also be specified via K8S_AUTH_KUBECONFIG - environment variable. - context: - description: - - The name of a context found in the config file. Can also be specified via K8S_AUTH_CONTEXT environment - variable. - host: - description: - - Provide a URL for accessing the API. Can also be specified via K8S_AUTH_HOST environment variable. - api_key: - description: - - Token used to authenticate with the API. Can also be specified via K8S_AUTH_API_KEY environment - variable. - username: - description: - - Provide a username for authenticating with the API. Can also be specified via K8S_AUTH_USERNAME - environment variable. - password: - description: - - Provide a password for authenticating with the API. Can also be specified via K8S_AUTH_PASSWORD - environment variable. - client_cert: - description: - - Path to a certificate used to authenticate with the API. Can also be specified via K8S_AUTH_CERT_FILE - environment variable. - aliases: [ cert_file ] - client_key: - description: - - Path to a key file used to authenticate with the API. Can also be specified via K8S_AUTH_KEY_FILE - environment variable. - aliases: [ key_file ] - ca_cert: - description: - - Path to a CA certificate used to authenticate with the API. Can also be specified via - K8S_AUTH_SSL_CA_CERT environment variable. - aliases: [ ssl_ca_cert ] - validate_certs: - description: - - "Whether or not to verify the API server's SSL certificates. Can also be specified via - K8S_AUTH_VERIFY_SSL environment variable." - type: bool - aliases: [ verify_ssl ] - namespaces: - description: - - List of namespaces. If not specified, will fetch all containers for all namespaces user is authorized - to access. - - requirements: - - "python >= 3.6" - - "kubernetes >= 12.0.0" - - "PyYAML >= 3.11" -""" - -EXAMPLES = """ -# File must be named openshift.yaml or openshift.yml - -- name: Authenticate with token, and return all pods and services for all namespaces - plugin: community.okd.openshift - connections: - - host: https://192.168.64.4:8443 - api_key: xxxxxxxxxxxxxxxx - verify_ssl: false - -- name: Use default config (~/.kube/config) file and active context, and return objects for a specific namespace - plugin: community.okd.openshift - connections: - - namespaces: - - testing - -- name: Use a custom config file, and a specific context. - plugin: community.okd.openshift - connections: - - kubeconfig: /path/to/config - context: 'awx/192-168-64-4:8443/developer' -""" - -try: - from ansible_collections.kubernetes.core.plugins.inventory.k8s import ( - K8sInventoryException, - InventoryModule as K8sInventoryModule, - format_dynamic_api_exc, - ) - from ansible_collections.kubernetes.core.plugins.module_utils.k8s.client import ( - get_api_client, - ) - - HAS_KUBERNETES_COLLECTION = True -except ImportError as e: - HAS_KUBERNETES_COLLECTION = False - - -try: - from kubernetes.dynamic.exceptions import DynamicApiError -except ImportError: - pass - - -class InventoryModule(K8sInventoryModule): - NAME = "community.okd.openshift" - - connection_plugin = "community.okd.oc" - transport = "oc" - - def check_kubernetes_collection(self): - if not HAS_KUBERNETES_COLLECTION: - raise K8sInventoryException( - "The kubernetes.core collection must be installed" - ) - - def fetch_objects(self, connections): - self.check_kubernetes_collection() - super(InventoryModule, self).fetch_objects(connections) - - self.display.deprecated( - "The 'openshift' inventory plugin has been deprecated and will be removed in release 5.0.0", - version="5.0.0", - collection_name="community.okd", - ) - - if connections: - if not isinstance(connections, list): - raise K8sInventoryException("Expecting connections to be a list.") - - for connection in connections: - client = get_api_client(**connection) - name = connection.get( - "name", self.get_default_host_name(client.configuration.host) - ) - if connection.get("namespaces"): - namespaces = connection["namespaces"] - else: - namespaces = self.get_available_namespaces(client) - for namespace in namespaces: - self.get_routes_for_namespace(client, name, namespace) - else: - client = get_api_client() - name = self.get_default_host_name(client.configuration.host) - namespaces = self.get_available_namespaces(client) - for namespace in namespaces: - self.get_routes_for_namespace(client, name, namespace) - - def get_routes_for_namespace(self, client, name, namespace): - self.check_kubernetes_collection() - v1_route = client.resources.get( - api_version="route.openshift.io/v1", kind="Route" - ) - try: - obj = v1_route.get(namespace=namespace) - except DynamicApiError as exc: - self.display.debug(exc) - raise K8sInventoryException( - "Error fetching Routes list: %s" % format_dynamic_api_exc(exc) - ) - - namespace_group = "namespace_{0}".format(namespace) - namespace_routes_group = "{0}_routes".format(namespace_group) - - self.inventory.add_group(name) - self.inventory.add_group(namespace_group) - self.inventory.add_child(name, namespace_group) - self.inventory.add_group(namespace_routes_group) - self.inventory.add_child(namespace_group, namespace_routes_group) - for route in obj.items: - route_name = route.metadata.name - route_annotations = ( - {} - if not route.metadata.annotations - else dict(route.metadata.annotations) - ) - - self.inventory.add_host(route_name) - - if route.metadata.labels: - # create a group for each label_value - for key, value in route.metadata.labels: - group_name = "label_{0}_{1}".format(key, value) - self.inventory.add_group(group_name) - self.inventory.add_child(group_name, route_name) - route_labels = dict(route.metadata.labels) - else: - route_labels = {} - - self.inventory.add_child(namespace_routes_group, route_name) - - # add hostvars - self.inventory.set_variable(route_name, "labels", route_labels) - self.inventory.set_variable(route_name, "annotations", route_annotations) - self.inventory.set_variable( - route_name, "cluster_name", route.metadata.clusterName - ) - self.inventory.set_variable(route_name, "object_type", "route") - self.inventory.set_variable( - route_name, "self_link", route.metadata.selfLink - ) - self.inventory.set_variable( - route_name, "resource_version", route.metadata.resourceVersion - ) - self.inventory.set_variable(route_name, "uid", route.metadata.uid) - - if route.spec.host: - self.inventory.set_variable(route_name, "host", route.spec.host) - - if route.spec.path: - self.inventory.set_variable(route_name, "path", route.spec.path) - - if hasattr(route.spec.port, "targetPort") and route.spec.port.targetPort: - self.inventory.set_variable(route_name, "port", dict(route.spec.port)) diff --git a/plugins/modules/openshift_adm_migrate_template_instances.py b/plugins/modules/openshift_adm_migrate_template_instances.py index ba3039d..0033e14 100644 --- a/plugins/modules/openshift_adm_migrate_template_instances.py +++ b/plugins/modules/openshift_adm_migrate_template_instances.py @@ -295,9 +295,9 @@ class OpenShiftMigrateTemplateInstances(AnsibleOpenshiftModule): object_type in transforms.keys() and obj["ref"].get("apiVersion") != transforms[object_type] ): - ti_elem["status"]["objects"][i]["ref"][ - "apiVersion" - ] = transforms[object_type] + ti_elem["status"]["objects"][i]["ref"]["apiVersion"] = ( + transforms[object_type] + ) ti_to_be_migrated.append(ti_elem) return ti_to_be_migrated diff --git a/plugins/modules/openshift_auth.py b/plugins/modules/openshift_auth.py index 6e213ab..62db3f7 100644 --- a/plugins/modules/openshift_auth.py +++ b/plugins/modules/openshift_auth.py @@ -225,7 +225,7 @@ def get_oauthaccesstoken_objectname_from_token(token_name): sha256Prefix = "sha256~" if token_name.startswith(sha256Prefix): - content = token_name[len(sha256Prefix):] + content = token_name[len(sha256Prefix) :] else: content = token_name b64encoded = urlsafe_b64encode(hashlib.sha256(content.encode()).digest()).rstrip( diff --git a/plugins/modules/openshift_route.py b/plugins/modules/openshift_route.py index ea73db7..8e95b43 100644 --- a/plugins/modules/openshift_route.py +++ b/plugins/modules/openshift_route.py @@ -421,9 +421,9 @@ class OpenShiftRoute(AnsibleOpenshiftModule): if tls_insecure_policy == "disallow": tls_insecure_policy = None else: - tls_ca_cert = ( - tls_cert - ) = tls_dest_ca_cert = tls_key = tls_insecure_policy = None + tls_ca_cert = tls_cert = tls_dest_ca_cert = tls_key = ( + tls_insecure_policy + ) = None route = { "apiVersion": "route.openshift.io/v1", diff --git a/tests/sanity/ignore-2.18.txt b/tests/sanity/ignore-2.18.txt new file mode 100644 index 0000000..2fd2bdc --- /dev/null +++ b/tests/sanity/ignore-2.18.txt @@ -0,0 +1,3 @@ +plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc +plugins/modules/k8s.py validate-modules:return-syntax-error +plugins/modules/openshift_process.py validate-modules:parameter-type-not-in-doc \ No newline at end of file diff --git a/tests/sanity/ignore-2.19.txt b/tests/sanity/ignore-2.19.txt new file mode 100644 index 0000000..2fd2bdc --- /dev/null +++ b/tests/sanity/ignore-2.19.txt @@ -0,0 +1,3 @@ +plugins/modules/k8s.py validate-modules:parameter-type-not-in-doc +plugins/modules/k8s.py validate-modules:return-syntax-error +plugins/modules/openshift_process.py validate-modules:parameter-type-not-in-doc \ No newline at end of file diff --git a/tox.ini b/tox.ini index 3c636f5..3217e2e 100644 --- a/tox.ini +++ b/tox.ini @@ -8,14 +8,14 @@ install_command = pip install {opts} {packages} [testenv:black] deps = - black >= 23.0, < 24.0 + black >= 25.0, < 26.0 commands = black {toxinidir}/plugins {toxinidir}/tests [testenv:ansible-lint] deps = - ansible-lint >= 24.7.0 + ansible-lint >= 25.1.2 changedir = {toxinidir} commands = ansible-lint @@ -24,10 +24,12 @@ commands = deps = flake8 {[testenv:black]deps} + {[testenv:ansible-lint]deps} commands = black -v --check --diff {toxinidir}/plugins {toxinidir}/tests flake8 {toxinidir} + ansible-lint [flake8] # E123, E125 skipped as they are invalid PEP-8.