Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
b108d23
[ci_gen_kustomize_values] Add multi-namespace SKMO scenario and playb…
vakwetu Mar 25, 2026
e556836
[ci_gen_kustomize_values] Add Cinder LVM backend support for multi-na…
vakwetu Mar 25, 2026
3802ea3
[federation] Support merging Keycloak CA into an existing CA bundle s…
vakwetu Mar 25, 2026
1626349
[federation] Fix broken 'until' condition waiting for RHSSO InstallPlan
vakwetu Mar 25, 2026
18926eb
[federation] Ensure kustomizations/controlplane directory exists befo…
vakwetu Mar 25, 2026
b573e86
[federation] Wait for Keystone Ready before running post-deploy auth …
vakwetu Mar 25, 2026
8af7be1
[federation] Directly patch OSCP with Keystone federation config
vakwetu Mar 25, 2026
f139f6f
[federation] Make OpenStack resource creation idempotent
vakwetu Mar 25, 2026
fc20382
[ci_gen_kustomize_values] Fix 3-run death spiral in edpm-nodeset2-val…
vakwetu Mar 25, 2026
7b45647
[multiple] Uniquify OSDPD names per deployment run
vakwetu Mar 25, 2026
48d4dc6
[multiple] Fix MCO stuck-uncordon deadlock
vakwetu Mar 25, 2026
f4a6aef
[multiple] Fix SKMO federation CA bundle handling and SSL trust
vakwetu Mar 25, 2026
587f9e5
[federation] Fix CA bundle secret name resolving to empty string
vakwetu Mar 25, 2026
12f2734
[multiple] Handle already-unlocked state in usroverlay task
vakwetu Mar 25, 2026
6767ae6
[multiple] Refactor inline Python/shell patterns to cleaner alternatives
vakwetu Mar 25, 2026
49572b5
[multiple] Use strategic merge patch in Keystone federation kustomiza…
vakwetu Mar 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions hooks/playbooks/skmo/configure-leaf-listener.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
- name: Patch leaf control plane with barbican-keystone-listener transport URL
hosts: localhost
gather_facts: false
vars:
central_namespace: openstack
leaf_namespace: openstack2
leaf_transport_url_name: rabbitmq-transport-url-barbican-keystone-listener-regiontwo
tasks:
- name: Get transport URL secret from central namespace
kubernetes.core.k8s_info:
api_version: v1
kind: Secret
namespace: "{{ central_namespace }}"
name: "{{ leaf_transport_url_name }}"
register: _transport_secret

- name: Patch OpenStackControlPlane in leaf region with notifications transport_url
vars:
_transport_url: "{{ _transport_secret.resources[0].data['transport_url'] | b64decode }}"
kubernetes.core.k8s:
state: patched
api_version: core.openstack.org/v1beta1
kind: OpenStackControlPlane
name: controlplane
namespace: "{{ leaf_namespace }}"
definition:
spec:
barbican:
template:
barbicanKeystoneListener:
customServiceConfig: |
[DEFAULT]
transport_url = {{ _transport_url }}
[keystone_notifications]
pool_name = barbican-listener-regionTwo
209 changes: 209 additions & 0 deletions hooks/playbooks/skmo/prepare-leaf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
---
- name: Prepare SKMO leaf prerequisites in regionZero
hosts: localhost
gather_facts: false
vars:
skmo_values_file: "{{ cifmw_architecture_repo }}/examples/va/multi-namespace-skmo/control-plane2/skmo-values.yaml"
osp_secrets_env_file: "{{ cifmw_architecture_repo }}/lib/control-plane/base/osp-secrets.env"
central_namespace: openstack
leaf_namespace: openstack2
leaf_secret_name: osp-secret
central_rootca_secret: rootca-public
central_rootca_internal_secret: rootca-internal
leaf_transport_url_name: barbican-keystone-listener-regiontwo
leaf_transport_url_name_secret: rabbitmq-transport-url-barbican-keystone-listener-regiontwo
leaf_transport_url_secret_copy: barbican-keystone-listener-regiontwo-transport
tasks:
- name: Wait for central Keystone API to be ready
kubernetes.core.k8s_info:
api_version: keystone.openstack.org/v1beta1
kind: KeystoneAPI
namespace: "{{ central_namespace }}"
register: _keystoneapi_info
retries: 60
delay: 10
until:
- _keystoneapi_info.resources | length > 0
- _keystoneapi_info.resources[0].status.conditions is defined
- _keystoneapi_info.resources[0].status.conditions |
selectattr('type', 'equalto', 'Ready') |
selectattr('status', 'equalto', 'True') | list | length > 0

- name: Wait for openstackclient pod to be ready in central region
kubernetes.core.k8s_info:
api_version: v1
kind: Pod
namespace: "{{ central_namespace }}"
name: openstackclient
register: _osc_pod_info
retries: 30
delay: 10
until:
- _osc_pod_info.resources | length > 0
- _osc_pod_info.resources[0].status.conditions is defined
- _osc_pod_info.resources[0].status.conditions |
selectattr('type', 'equalto', 'Ready') |
selectattr('status', 'equalto', 'True') | list | length > 0

- name: Load SKMO values
ansible.builtin.set_fact:
skmo_values: "{{ lookup('file', skmo_values_file) | from_yaml }}"

- name: Set SKMO leaf facts
ansible.builtin.set_fact:
leaf_region: "{{ skmo_values.data.leafRegion }}"
leaf_admin_user: "{{ skmo_values.data.leafAdminUser }}"
leaf_admin_project: "{{ skmo_values.data.leafAdminProject }}"
leaf_admin_password_key: "{{ skmo_values.data.leafAdminPasswordKey }}"
keystone_internal_url: "{{ skmo_values.data.keystoneInternalURL }}"
keystone_public_url: "{{ skmo_values.data.keystonePublicURL }}"
ca_bundle_secret_name: "{{ skmo_values.data.leafCaBundleSecretName }}"

- name: Ensure leaf osp-secret exists (pre-create from env file)
ansible.builtin.shell: |
set -euo pipefail
if ! oc -n {{ leaf_namespace }} get secret {{ leaf_secret_name }} >/dev/null 2>&1; then
oc -n {{ leaf_namespace }} create secret generic {{ leaf_secret_name }} \
--from-env-file="{{ osp_secrets_env_file }}" \
--dry-run=client -o yaml | oc apply -f -
fi
args:
executable: /bin/bash

- name: Read leaf admin password from leaf secret
ansible.builtin.shell: |
set -euo pipefail
oc -n {{ leaf_namespace }} get secret {{ leaf_secret_name }} \
-o jsonpath='{.data.{{ leaf_admin_password_key }}}' | base64 -d
args:
executable: /bin/bash
register: leaf_admin_password
changed_when: false

- name: Ensure leaf region exists in central Keystone
ansible.builtin.shell: |
set -euo pipefail
oc -n {{ central_namespace }} rsh openstackclient \
openstack region show {{ leaf_region }} >/dev/null 2>&1 || \
oc -n {{ central_namespace }} rsh openstackclient \
openstack region create {{ leaf_region }}
args:
executable: /bin/bash

- name: Ensure keystone catalog endpoints exist for leaf region
ansible.builtin.shell: |
set -euo pipefail
if ! oc -n {{ central_namespace }} rsh openstackclient \
openstack endpoint list --service keystone --interface public --region {{ leaf_region }} \
-f value -c ID | head -1 | grep -q .; then
oc -n {{ central_namespace }} rsh openstackclient \
openstack endpoint create --region {{ leaf_region }} identity public "{{ keystone_public_url }}"
fi
if ! oc -n {{ central_namespace }} rsh openstackclient \
openstack endpoint list --service keystone --interface internal --region {{ leaf_region }} \
-f value -c ID | head -1 | grep -q .; then
oc -n {{ central_namespace }} rsh openstackclient \
openstack endpoint create --region {{ leaf_region }} identity internal "{{ keystone_internal_url }}"
fi
args:
executable: /bin/bash

- name: Ensure leaf admin project exists in central Keystone
ansible.builtin.shell: |
set -euo pipefail
oc -n {{ central_namespace }} rsh openstackclient \
openstack project show {{ leaf_admin_project }} >/dev/null 2>&1 || \
oc -n {{ central_namespace }} rsh openstackclient \
openstack project create {{ leaf_admin_project }}
args:
executable: /bin/bash

- name: Ensure leaf admin user exists and has admin role
ansible.builtin.shell: |
set -euo pipefail
if ! oc -n {{ central_namespace }} rsh openstackclient \
openstack user show {{ leaf_admin_user }} >/dev/null 2>&1; then
oc -n {{ central_namespace }} rsh openstackclient \
openstack user create --domain Default --password "{{ leaf_admin_password.stdout | trim }}" {{ leaf_admin_user }}
fi
oc -n {{ central_namespace }} rsh openstackclient \
openstack role add --project {{ leaf_admin_project }} --user {{ leaf_admin_user }} admin
args:
executable: /bin/bash

- name: Get existing leaf CA bundle secret if present
kubernetes.core.k8s_info:
api_version: v1
kind: Secret
namespace: "{{ leaf_namespace }}"
name: "{{ ca_bundle_secret_name }}"
register: _existing_bundle

- name: Get central rootca certs
kubernetes.core.k8s_info:
api_version: v1
kind: Secret
namespace: "{{ central_namespace }}"
name: "{{ item }}"
register: _central_certs
loop:
- "{{ central_rootca_secret }}"
- "{{ central_rootca_internal_secret }}"

- name: Create or update leaf CA bundle secret
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: "{{ ca_bundle_secret_name }}"
namespace: "{{ leaf_namespace }}"
data: "{{ (_existing_bundle.resources[0].data | default({})) | combine({
'skmo-central-rootca.crt': _central_certs.results[0].resources[0].data['tls.crt'],
'skmo-central-rootca-internal.crt': _central_certs.results[1].resources[0].data['tls.crt']
}) }}"

- name: Create TransportURL CR in central region for leaf listener
ansible.builtin.shell: |
set -euo pipefail
oc apply -f - <<EOF
apiVersion: rabbitmq.openstack.org/v1beta1
kind: TransportURL
metadata:
name: {{ leaf_transport_url_name }}
namespace: {{ central_namespace }}
spec:
rabbitmqClusterName: rabbitmq
EOF
args:
executable: /bin/bash

- name: Wait for TransportURL to be ready
ansible.builtin.shell: |
set -euo pipefail
oc wait transporturl {{ leaf_transport_url_name }} \
-n {{ central_namespace }} \
--for=condition=Ready --timeout=120s
args:
executable: /bin/bash

- name: Get transport URL secret from central namespace
kubernetes.core.k8s_info:
api_version: v1
kind: Secret
namespace: "{{ central_namespace }}"
name: "{{ leaf_transport_url_name_secret }}"
register: _transport_secret

- name: Copy transport URL secret to leaf namespace
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: "{{ leaf_transport_url_secret_copy }}"
namespace: "{{ leaf_namespace }}"
type: "{{ _transport_secret.resources[0].type }}"
data: "{{ _transport_secret.resources[0].data }}"
139 changes: 139 additions & 0 deletions hooks/playbooks/skmo/update-central-ca-bundle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
---
- name: Update central CA bundle with leaf region CAs and wait for reconciliation
hosts: localhost
gather_facts: false
vars:
central_namespace: openstack
leaf_namespace: openstack2
controlplane_name: controlplane
leaf_rootca_secret: rootca-public
leaf_rootca_internal_secret: rootca-internal
tasks:
# -------------------------------------------------------------------------
# Step 1 - determine which secret holds the central CA bundle.
#
# Priority:
# 1. spec.tls.caBundleSecretName already set on the OSCP.
# 2. cifmw_custom_ca_certs_secret_name variable (if set by caller).
# 3. Hard default: "custom-ca-certs".
# -------------------------------------------------------------------------
- name: Read current OpenStackControlPlane state
kubernetes.core.k8s_info:
api_version: core.openstack.org/v1beta1
kind: OpenStackControlPlane
name: "{{ controlplane_name }}"
namespace: "{{ central_namespace }}"
register: _central_oscp_info

- name: Resolve CA bundle secret name
ansible.builtin.set_fact:
_ca_bundle_secret_name: >-
{{
((_central_oscp_info.resources | first).spec.tls | default({})).caBundleSecretName
| default(cifmw_custom_ca_certs_secret_name | default('custom-ca-certs', true), true)
| default('custom-ca-certs', true)
}}
_oscp_has_ca_bundle: >-
{{
(
((_central_oscp_info.resources | first).spec.tls | default({})).caBundleSecretName
| default('')
) | length > 0
}}

# -------------------------------------------------------------------------
# Step 2 - fetch the leaf region CA certs
# -------------------------------------------------------------------------
- name: Get leaf region rootca certs
kubernetes.core.k8s_info:
api_version: v1
kind: Secret
namespace: "{{ leaf_namespace }}"
name: "{{ item }}"
register: _leaf_certs
loop:
- "{{ leaf_rootca_secret }}"
- "{{ leaf_rootca_internal_secret }}"

# -------------------------------------------------------------------------
# Step 3 - get existing central CA bundle data (if secret already exists)
# -------------------------------------------------------------------------
- name: Look up existing central CA bundle secret
kubernetes.core.k8s_info:
api_version: v1
kind: Secret
namespace: "{{ central_namespace }}"
name: "{{ _ca_bundle_secret_name }}"
register: _existing_bundle

- name: Capture existing CA bundle secret data
ansible.builtin.set_fact:
_existing_bundle_data: >-
{{
(_existing_bundle.resources | first).data
if _existing_bundle.resources | length > 0
else {}
}}

# -------------------------------------------------------------------------
# Step 4 - create or update the secret, merging in the leaf CAs
# -------------------------------------------------------------------------
- name: Create or update central CA bundle secret with leaf region CAs
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Secret
metadata:
name: "{{ _ca_bundle_secret_name }}"
namespace: "{{ central_namespace }}"
data: >-
{{
_existing_bundle_data | combine({
'skmo-leaf-rootca.crt':
_leaf_certs.results[0].resources[0].data['tls.crt'],
'skmo-leaf-rootca-internal.crt':
_leaf_certs.results[1].resources[0].data['tls.crt']
})
}}

# -------------------------------------------------------------------------
# Step 5 - patch the OSCP to reference the secret when not already set
# -------------------------------------------------------------------------
- name: Patch OpenStackControlPlane to set caBundleSecretName (when unset)
when: not _oscp_has_ca_bundle | bool
kubernetes.core.k8s:
state: patched
definition:
apiVersion: core.openstack.org/v1beta1
kind: OpenStackControlPlane
metadata:
name: "{{ controlplane_name }}"
namespace: "{{ central_namespace }}"
spec:
tls:
caBundleSecretName: "{{ _ca_bundle_secret_name }}"

# -------------------------------------------------------------------------
# Step 6 - wait for RHOSO to reconcile combined-ca-bundle.
#
# We compare the fingerprint of the leaf rootca cert we just added against
# every cert in combined-ca-bundle, retrying until it appears.
# -------------------------------------------------------------------------
- name: Wait for leaf region CA to appear in combined-ca-bundle
kubernetes.core.k8s_info:
api_version: v1
kind: Secret
namespace: "{{ central_namespace }}"
name: combined-ca-bundle
register: _combined_bundle
until: >-
(_combined_bundle.resources | length > 0) and
(
_leaf_certs.results[0].resources[0].data['tls.crt'] | b64decode
in
(_combined_bundle.resources | first).data['tls-ca-bundle.pem'] | b64decode
)
retries: 30
delay: 10
changed_when: false
Loading
Loading