From 3e57b7f2c8cee2cec5d8cc0f674cfb14a1330bc1 Mon Sep 17 00:00:00 2001 From: Akos Eros Date: Thu, 8 Jan 2026 09:49:20 +0100 Subject: [PATCH] feat: Refactor acm_import role The refactor is needed for the import to work properly with hypershift spoke clusters as well. --- acm_import/post.yml | 16 --- .../tasks/hub-acm-add-regional-cluster.yml | 128 +++++++----------- acm_import/roles/acm_import/tasks/main.yml | 13 ++ .../acm_import/tasks/region-fetch-api.yml | 8 +- .../templates/autoimport_secret.yml.j2 | 13 ++ 5 files changed, 77 insertions(+), 101 deletions(-) create mode 100644 acm_import/roles/acm_import/templates/autoimport_secret.yml.j2 diff --git a/acm_import/post.yml b/acm_import/post.yml index a957a18..d03ab41 100644 --- a/acm_import/post.yml +++ b/acm_import/post.yml @@ -8,21 +8,5 @@ HUBCONFIG: "{{ lookup('env', 'HUBCONFIG') }}" SPOKECONFIG: "{{ lookup('env', 'SPOKECONFIG') }}" - - name: Check for correct HUBCONFIG env variable - ansible.builtin.fail: - msg: "HUBCONFIG env variable needs to be set and pointing to the HUB kubeconfig file" - when: - HUBCONFIG is not defined or HUBCONFIG | length == 0 - - - name: Check for correct SPOKECONFIG env variable - ansible.builtin.fail: - msg: "SPOKECONFIG env variable needs to be set and pointing to the SPOKE kubeconfig file" - when: - SPOKECONFIG is not defined or SPOKECONFIG | length == 0 - - - name: Print the hub and spoke kubeconfigs - ansible.builtin.debug: - msg: "HUBCONFIG: {{ HUBCONFIG }} - SPOKECONFIG: {{ SPOKECONFIG }}" - roles: - acm_import diff --git a/acm_import/roles/acm_import/tasks/hub-acm-add-regional-cluster.yml b/acm_import/roles/acm_import/tasks/hub-acm-add-regional-cluster.yml index 15c09d2..cf7579c 100644 --- a/acm_import/roles/acm_import/tasks/hub-acm-add-regional-cluster.yml +++ b/acm_import/roles/acm_import/tasks/hub-acm-add-regional-cluster.yml @@ -4,91 +4,63 @@ fail_msg: "region_cluster_name failed regex validation" success_msg: "{{ region_cluster_name }} is valid" -- name: "Check if ManagedCluster is registered" +- name: "Create regional ManagedCluster" + kubernetes.core.k8s: + state: present + template: managedcluster.yml.j2 + +- name: Ensure regional cluster namespace has been created kubernetes.core.k8s_info: - kubeconfig: "{{ HUBCONFIG }}" - api_version: cluster.open-cluster-management.io/v1 - kind: ManagedCluster + api: v1 + kind: Namespace name: "{{ region_cluster_name }}" - register: ocm_mc_obj - -- name: "Set fact if regional cluster is registered" - ansible.builtin.set_fact: - ocm_mc_registered: "{{ ocm_mc_obj.resources | length == 1 }}" + register: _acm_namespace_status + until: + - _acm_namespace_status.resources | length > 0 + retries: 60 + delay: 10 -- name: Register Managed Cluster - when: - - not ocm_mc_registered +# To import the spoke cluster to the hub cluster, we need to create +# a secret that contains the spoke cluster's kubeconfig. +# This requires a special formatting, including double line breaks +# and a correct indentation. +- name: Create autoimport secret block: - - name: "Create regional ManagedCluster" - kubernetes.core.k8s: - state: present - kubeconfig: "{{ HUBCONFIG }}" - template: managedcluster.yml.j2 - wait: true - wait_condition: - type: 'HubAcceptedManagedCluster' - reason: 'HubClusterAdminAccepted' - status: 'True' - wait_timeout: 60 - register: create_regional - until: create_regional is not failed - retries: 10 - delay: 10 - - - name: "Create regional KlusterAddonConfig" - kubernetes.core.k8s: - state: present - kubeconfig: "{{ HUBCONFIG }}" - template: klusterletaddonconfig.yml.j2 - -- name: "Pull ManagedClusterJoined Status" - ansible.builtin.set_fact: - ocm_mc_status: > - "{{ lookup('kubernetes.core.k8s', kubeconfig=HUBCONFIG, api_version='cluster.open-cluster-management.io/v1', - kind='ManagedCluster', resource_name=region_cluster_name) | json_query(query) }}" - vars: - query: "status.conditions[?type == 'ManagedClusterJoined'].status" - -- name: "Determine if Managed Cluster has Joined" - ansible.builtin.set_fact: - ocm_mc_reported: "{{ ocm_mc_status | length == 1 and ocm_mc_status[0] == 'True' }}" + - name: Get kubeconfig content + ansible.builtin.slurp: + src: "{{ SPOKECONFIG }}" + register: _acm_region_kubeconfig_content + no_log: true -- name: Attach Managed Cluster - when: not ocm_mc_reported - block: - - name: "[HUB] Derive import name" + - name: Save kubeconfig content in a variable ansible.builtin.set_fact: - cluster_import: "{{ region_cluster_name }}-import" + acm_region_kubeconfig: "{{ _acm_region_kubeconfig_content['content'] | b64decode }}" + no_log: true - - name: "[HUB] Get CRDs from Hub for the new ManagedCluster" - ansible.builtin.set_fact: - import_crds: > - "{{ lookup('k8s', kubeconfig=HUBCONFIG, api_version='v1', kind='Secret', namespace=region_cluster_name, - resource_name=cluster_import) | json_query(query) }}" - vars: - query: "data.\"crds.yaml\"" - - - name: "[HUB] Get ROs from Hub for the new ManagedCluster" - ansible.builtin.set_fact: - import_ros: > - "{{ lookup('k8s', kubeconfig=HUBCONFIG, api_version='v1', kind='Secret', namespace=region_cluster_name, - resource_name=cluster_import) | json_query(query) }}" - vars: - query: "data.\"import.yaml\"" - - - name: "[MC] Apply CRDs to new ManagedCluster" + - name: Apply Secret to attach spoke cluster to hub cluster kubernetes.core.k8s: state: present - kubeconfig: "{{ SPOKECONFIG }}" - definition: "{{ import_crds | b64decode }}" - retries: 10 - delay: 20 + definition: "{{ lookup('ansible.builtin.template', 'autoimport_secret.yml.j2') | from_yaml }}" + no_log: true - - name: "[MC] Apply ROs to new ManagedCluster" - kubernetes.core.k8s: - state: present - kubeconfig: "{{ SPOKECONFIG }}" - definition: "{{ import_ros | b64decode }}" - retries: 10 - delay: 20 +# To correctly join the hub cluster, ManagedClusterJoined condition must be True. +- name: Ensure ManagedCluster has joined the hub cluster + kubernetes.core.k8s_info: + api: cluster.open-cluster-management.io/v1 + kind: ManagedCluster + name: "{{ region_cluster_name }}" + register: _acm_managedcluster_status + vars: + _acm_status_query: "resources[0].status.conditions[?type=='ManagedClusterJoined'].status" + _acm_update_status: "{{ _acm_managedcluster_status | json_query(_acm_status_query) | flatten | unique }}" # noqa: jinja[invalid] + until: + - _acm_managedcluster_status.resources is defined + - _acm_managedcluster_status.resources | length > 0 + - _acm_update_status == ['True'] + retries: 30 + delay: 10 + +- name: "Create regional KlusterAddonConfig" + kubernetes.core.k8s: + state: present + template: klusterletaddonconfig.yml.j2 diff --git a/acm_import/roles/acm_import/tasks/main.yml b/acm_import/roles/acm_import/tasks/main.yml index 242d633..3354797 100644 --- a/acm_import/roles/acm_import/tasks/main.yml +++ b/acm_import/roles/acm_import/tasks/main.yml @@ -1,3 +1,11 @@ +- name: Assert the required variables are defined + ansible.builtin.assert: + that: + - SPOKECONFIG is defined + - SPOKECONFIG | length > 0 + - HUBCONFIG is defined + - HUBCONFIG | length > 0 + - name: Output configs ansible.builtin.debug: msg: "HUBCONFIG: {{ HUBCONFIG }} - SPOKECONFIG: {{ SPOKECONFIG }}" @@ -8,7 +16,12 @@ apply: environment: KUBECONFIG: "{{ SPOKECONFIG }}" + when: + - region_cluster_name is not defined - name: Add the regional cluster to the ACM hub ansible.builtin.include_tasks: file: hub-acm-add-regional-cluster.yml + apply: + environment: + KUBECONFIG: "{{ HUBCONFIG }}" diff --git a/acm_import/roles/acm_import/tasks/region-fetch-api.yml b/acm_import/roles/acm_import/tasks/region-fetch-api.yml index 4315122..729c4c2 100644 --- a/acm_import/roles/acm_import/tasks/region-fetch-api.yml +++ b/acm_import/roles/acm_import/tasks/region-fetch-api.yml @@ -6,17 +6,11 @@ ansible.builtin.set_fact: region_api: "{{ cluster_info['connection']['host'] }}" -# FIXME(bandini): This currently seems the sanest way to be sure of the cluster name being used as k8s has no concept -# of own cluster name and other hacks like parsing the API url seem worse than this - name: Find regional cluster name ansible.builtin.shell: | - {% raw %} - oc config view --raw -o go-template="{{ range .clusters }}{{ if and (eq .cluster.server \"${API}\") (index .cluster \"certificate-authority-data\") }}{{ .name }}{{ end }}{{ end }}" - {% endraw %} + oc get dns cluster -o jsonpath='{.spec.baseDomain}{"\n"}' | cut -d. -f1 register: region_cluster_oc changed_when: false - environment: - API: "{{ region_api }}" - name: Set regional cluster name fact ansible.builtin.set_fact: diff --git a/acm_import/roles/acm_import/templates/autoimport_secret.yml.j2 b/acm_import/roles/acm_import/templates/autoimport_secret.yml.j2 new file mode 100644 index 0000000..3bfcffb --- /dev/null +++ b/acm_import/roles/acm_import/templates/autoimport_secret.yml.j2 @@ -0,0 +1,13 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: auto-import-secret + namespace: {{ region_cluster_name }} +stringData: + autoImportRetry: "2" + kubeconfig: > + +{{ acm_region_kubeconfig | regex_replace('\n', '\n\n') | indent(4, True) }} + +type: Opaque