Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
160 changes: 160 additions & 0 deletions hooks/playbooks/skmo/configure-leaf-keystone-internal.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
---
# Patch the leaf/workload region OpenStackControlPlane to use the Skupper
# Listener virtual endpoint for internal Keystone authentication traffic.
#
# The public endpoint override is left unchanged so that end-user traffic and
# the Keystone service catalog continue to reference the central region's
# external (public) URL. Only the *internal* override — used for all
# service-to-service communication inside the workload namespace — is updated
# to point at the Skupper Listener.
#
# After the OSCP is updated, this playbook also ensures that EDPM compute
# nodes can resolve the Skupper Keystone virtual service name. The Skupper
# Listener creates a ClusterIP-only Service (keystone-regionone) that is not
# reachable from outside the OCP cluster. EDPM nodes use the dnsmasq
# LoadBalancer Service (in the leaf namespace) as their DNS server and require
# a resolvable hostname for the Keystone auth_url configured in nova.conf.
# To bridge this gap, the playbook:
# 1. Creates a dedicated MetalLB LoadBalancer Service that selects the
# Skupper router pod and exposes port 5000 on the leaf internalapi network.
# 2. Creates a DNSData CR so dnsmasq resolves both the short (.svc) and
# fully-qualified (.svc.cluster.local) names to the LoadBalancer IP.
#
# This playbook is a no-op when cifmw_skupper_keystone_enabled is false. In
# that case, skupper-keystone-connector.yaml (pre_stage hook) has already
# written the public Keystone URL into skmo-values.yaml so the leaf OSCP is
# created with the public endpoint — no Skupper tunnel is used and there is no
# matching Connector for the Skupper Listener, so the OSCP must not be patched
# to point at the Skupper virtual Service.
#
# Run skupper-keystone-connector.yaml and skupper-keystone-listener.yaml
# before this playbook so that the Skupper virtual service is in place.
#
# Variables:
# cifmw_skupper_keystone_enabled (default: true)
# When false, all tasks in this playbook are skipped.
# cifmw_skupper_leaf_namespace (default: openstack2)
# cifmw_skupper_keystone_listener_host (default: keystone-regionone)
# cifmw_skupper_keystone_port (default: 5000)
# cifmw_skupper_keystone_metallb_pool (default: internalapi2)
# MetalLB address-pool name for the leaf internalapi network. An IP is
# auto-assigned from this pool; no static address is required.
- name: Configure leaf region to use Skupper Keystone internal endpoint
hosts: "{{ cifmw_target_hook_host | default('localhost') }}"
gather_facts: false
vars:
cifmw_skupper_keystone_enabled: true
cifmw_skupper_leaf_namespace: openstack2
cifmw_skupper_keystone_listener_host: keystone-regionone
cifmw_skupper_keystone_port: 5000
cifmw_skupper_keystone_metallb_pool: internalapi2
tasks:
- name: Skip all tasks when Skupper Keystone routing is disabled
ansible.builtin.meta: end_play
when: not cifmw_skupper_keystone_enabled | bool

- name: Build the Skupper Keystone internal URL
ansible.builtin.set_fact:
_skupper_keystone_internal_url: >-
https://{{ cifmw_skupper_keystone_listener_host }}.{{ cifmw_skupper_leaf_namespace }}.svc.cluster.local:{{ cifmw_skupper_keystone_port }}

- name: Patch leaf OSCP internal Keystone override to use Skupper endpoint
# This switches the internal keystone endpoint URL from the central
# region's public URL to the Skupper Listener virtual service. The
# public endpoint override is not touched.
kubernetes.core.k8s:
state: patched
api_version: core.openstack.org/v1beta1
kind: OpenStackControlPlane
name: controlplane
namespace: "{{ cifmw_skupper_leaf_namespace }}"
definition:
spec:
keystone:
template:
override:
service:
internal:
endpointURL: "{{ _skupper_keystone_internal_url }}"

- name: Wait for leaf OSCP to reconcile after Keystone endpoint change
kubernetes.core.k8s_info:
api_version: core.openstack.org/v1beta1
kind: OpenStackControlPlane
name: controlplane
namespace: "{{ cifmw_skupper_leaf_namespace }}"
register: _leaf_oscp
retries: 60
delay: 30
until:
- _leaf_oscp.resources | length > 0
- _leaf_oscp.resources[0].status is defined
- _leaf_oscp.resources[0].status.conditions is defined
- _leaf_oscp.resources[0].status.conditions |
selectattr('type', 'equalto', 'Ready') |
selectattr('status', 'equalto', 'True') | list | length > 0

- name: Create LoadBalancer service to expose Skupper Keystone for EDPM nodes
# The Skupper Listener creates a ClusterIP-only Service that EDPM nodes
# outside the OCP cluster cannot reach. This LoadBalancer Service selects
# the same Skupper router pod and obtains a MetalLB IP on the leaf
# internalapi network, making port 5000 reachable from EDPM compute nodes.
kubernetes.core.k8s:
state: present
definition:
apiVersion: v1
kind: Service
metadata:
name: "{{ cifmw_skupper_keystone_listener_host }}-lb"
namespace: "{{ cifmw_skupper_leaf_namespace }}"
annotations:
metallb.universe.tf/address-pool: "{{ cifmw_skupper_keystone_metallb_pool }}"
spec:
type: LoadBalancer
selector:
application: skupper-router
skupper.io/component: router
ports:
- name: keystone-internal
port: "{{ cifmw_skupper_keystone_port | int }}"
protocol: TCP
targetPort: 1024

- name: Wait for MetalLB to assign an external IP to the keystone LoadBalancer
kubernetes.core.k8s_info:
api_version: v1
kind: Service
name: "{{ cifmw_skupper_keystone_listener_host }}-lb"
namespace: "{{ cifmw_skupper_leaf_namespace }}"
register: _keystone_lb_svc
retries: 12
delay: 10
until:
- _keystone_lb_svc.resources | length > 0
- _keystone_lb_svc.resources[0].status.loadBalancer.ingress is defined
- _keystone_lb_svc.resources[0].status.loadBalancer.ingress | length > 0

- name: Set keystone LoadBalancer IP fact
ansible.builtin.set_fact:
_keystone_lb_ip: >-
{{ _keystone_lb_svc.resources[0].status.loadBalancer.ingress[0].ip }}

- name: Create DNSData entry for Skupper Keystone endpoint
# Adds both the short (.svc) and fully-qualified (.svc.cluster.local)
# names to the dnsmasq instance serving EDPM nodes, so that nova-compute
# auth_url lookups resolve to the LoadBalancer IP above.
kubernetes.core.k8s:
state: present
definition:
apiVersion: network.openstack.org/v1beta1
kind: DNSData
metadata:
name: keystone-skupper
namespace: "{{ cifmw_skupper_leaf_namespace }}"
spec:
dnsDataLabelSelectorValue: dnsdata
Comment thread
vakwetu marked this conversation as resolved.
hosts:
- hostnames:
- "{{ cifmw_skupper_keystone_listener_host }}.{{ cifmw_skupper_leaf_namespace }}.svc"
- "{{ cifmw_skupper_keystone_listener_host }}.{{ cifmw_skupper_leaf_namespace }}.svc.cluster.local"
ip: "{{ _keystone_lb_ip }}"
56 changes: 42 additions & 14 deletions hooks/playbooks/skmo/configure-leaf-listener.yaml
Original file line number Diff line number Diff line change
@@ -1,36 +1,64 @@
---
- name: Patch leaf control plane with barbican-keystone-listener transport URL
hosts: localhost
# Configure the leaf barbican-keystone-listener to use the Skupper
# application network for cross-region RabbitMQ access.
#
# In the leaf region:
# - Read the RabbitMQ credentials from the dedicated user credentials secret
# created by the RabbitMQ operator when the TransportURL CR is reconciled.
# - Patch barbicanKeystoneListener to connect to the central RabbitMQ via the
# Skupper Listener endpoint using those credentials and its own pool_name.
#
# Variables:
# cifmw_skupper_central_namespace (default: openstack)
# cifmw_skupper_leaf_namespace (default: openstack2)
# cifmw_skupper_listener_host (default: rabbitmq-regionone)
# Must match the host set in skupper-listener.yaml.
# cifmw_skupper_rabbitmq_port (default: 5671)
# cifmw_skupper_transport_url_name (default: barbican-keystone-listener-regiontwo)
# Name of the TransportURL CR created in prepare-leaf.yaml. The operator
# creates a user credentials secret named:
# rabbitmq-user-<name>-<username>-user
# cifmw_skupper_transport_url_username (default: barbican-keystone-listener-regiontwo)
# Must match the username field set on the TransportURL CR in prepare-leaf.yaml.
- name: Configure barbican-keystone-listener to use Skupper for cross-region RabbitMQ
hosts: "{{ cifmw_target_hook_host | default('localhost') }}"
gather_facts: false
vars:
central_namespace: openstack
leaf_namespace: openstack2
leaf_transport_url_name: rabbitmq-transport-url-barbican-keystone-listener-regiontwo
cifmw_skupper_central_namespace: openstack
cifmw_skupper_leaf_namespace: openstack2
cifmw_skupper_listener_host: rabbitmq-regionone
cifmw_skupper_rabbitmq_port: 5671
cifmw_skupper_transport_url_name: barbican-keystone-listener-regiontwo
cifmw_skupper_transport_url_username: barbican-keystone-listener-regiontwo
tasks:
- name: Get transport URL secret from central namespace
- name: Get RabbitMQ user credentials secret for leaf listener
# The RabbitMQ operator creates a secret named
# rabbitmq-user-<transport-url-name>-<username>-user that contains
# the username and password fields for the dedicated RabbitMQ user.
kubernetes.core.k8s_info:
api_version: v1
kind: Secret
namespace: "{{ central_namespace }}"
name: "{{ leaf_transport_url_name }}"
register: _transport_secret
namespace: "{{ cifmw_skupper_central_namespace }}"
name: "rabbitmq-user-{{ cifmw_skupper_transport_url_name }}-{{ cifmw_skupper_transport_url_username }}-user"
register: _rabbitmq_user_secret

- name: Patch OpenStackControlPlane in leaf region with notifications transport_url
- name: Patch leaf barbicanKeystoneListener to use Skupper RabbitMQ endpoint
vars:
_transport_url: "{{ _transport_secret.resources[0].data['transport_url'] | b64decode }}"
_username: "{{ _rabbitmq_user_secret.resources[0].data['username'] | b64decode }}"
_password: "{{ _rabbitmq_user_secret.resources[0].data['password'] | b64decode }}"
kubernetes.core.k8s:
state: patched
api_version: core.openstack.org/v1beta1
kind: OpenStackControlPlane
name: controlplane
namespace: "{{ leaf_namespace }}"
namespace: "{{ cifmw_skupper_leaf_namespace }}"
definition:
spec:
barbican:
template:
barbicanKeystoneListener:
customServiceConfig: |
[DEFAULT]
transport_url = {{ _transport_url }}
transport_url = rabbit://{{ _username }}:{{ _password }}@{{ cifmw_skupper_listener_host }}:{{ cifmw_skupper_rabbitmq_port }}/?ssl=1
[keystone_notifications]
pool_name = barbican-listener-regionTwo
pool_name = barbican-listener-regiontwo
Comment thread
danpawlik marked this conversation as resolved.
25 changes: 3 additions & 22 deletions hooks/playbooks/skmo/prepare-leaf.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
- name: Prepare SKMO leaf prerequisites in regionZero
hosts: localhost
hosts: "{{ cifmw_target_hook_host | default('localhost') }}"
gather_facts: false
vars:
skmo_values_file: "{{ cifmw_architecture_repo }}/examples/va/multi-namespace-skmo/control-plane2/skmo-values.yaml"
Expand All @@ -10,8 +10,8 @@
central_rootca_secret: rootca-public
central_rootca_internal_secret: rootca-internal
leaf_transport_url_name: barbican-keystone-listener-regiontwo
leaf_transport_url_username: 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:
Expand Down Expand Up @@ -159,6 +159,7 @@
namespace: "{{ central_namespace }}"
spec:
rabbitmqClusterName: rabbitmq
username: "{{ leaf_transport_url_username }}"

- name: Wait for TransportURL to be ready
kubernetes.core.k8s_info:
Expand All @@ -176,23 +177,3 @@
- _transport_url_info.resources[0].status.conditions |
selectattr('type', 'equalto', 'Ready') |
selectattr('status', 'equalto', 'True') | list | length > 0

- 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 }}"
51 changes: 51 additions & 0 deletions hooks/playbooks/skmo/skupper-connector-tasks.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
# Shared task file: Create a Skupper Connector CR and wait for it to be
# Configured. Include this from service-specific connector playbooks after
# the TLS credentials have been discovered and stored in the variables below.
#
# Expected variables (set via include_tasks vars: block):
# _cifmw_connector_name Skupper Connector CR name
# _cifmw_connector_namespace Namespace for the Connector
# _cifmw_connector_routing_key Skupper routing key (must match Listener)
# _cifmw_connector_host Backend service hostname
# _cifmw_connector_port Backend service port
# _cifmw_connector_tls_credentials Name of the TLS Secret for the backend
# _cifmw_connector_verify_hostname Whether Skupper verifies the backend cert hostname
# _cifmw_connector_ignore_wait_errors Whether to ignore wait failures

- name: Create Skupper Connector
kubernetes.core.k8s:
state: present
definition:
apiVersion: skupper.io/v2alpha1
kind: Connector
metadata:
name: "{{ _cifmw_connector_name }}"
namespace: "{{ _cifmw_connector_namespace }}"
spec:
routingKey: "{{ _cifmw_connector_routing_key }}"
host: "{{ _cifmw_connector_host }}"
port: "{{ _cifmw_connector_port }}"
type: tcp
tlsCredentials: "{{ _cifmw_connector_tls_credentials }}"
verifyHostname: "{{ _cifmw_connector_verify_hostname }}"

- name: Wait for Skupper Connector to be configured
# A Connector shows "Ready" only after a matching Listener is deployed in
# the remote namespace. Waiting for "Configured" is sufficient here.
ignore_errors: "{{ _cifmw_connector_ignore_wait_errors | bool }}"
kubernetes.core.k8s_info:
api_version: skupper.io/v2alpha1
kind: Connector
name: "{{ _cifmw_connector_name }}"
namespace: "{{ _cifmw_connector_namespace }}"
register: _connector
retries: 30
delay: 10
until:
- _connector.resources | length > 0
- _connector.resources[0].status is defined
- _connector.resources[0].status.conditions is defined
- _connector.resources[0].status.conditions |
selectattr('type', 'equalto', 'Configured') |
selectattr('status', 'equalto', 'True') | list | length > 0
Loading
Loading