Repository: innabox/cloudkit-aap
Pull request #15: Add openstack/esi integration roles
Author: login: larsks
Created: 2025-04-09T14:35:14Z | Merged: 2025-04-14T13:57:26Z

Add roles to set up floating ip addresses, port forwarding, and dns records for cluster api and ingress endpoints.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Expanded cloud automation support with new integration collections and dependencies: `openstack.cloud`, `amazon.aws`, `community.general`, and `ansible.utils`.
  - Introduced dynamic role-based workflows to automate the creation and deletion of hosted clusters.
  - Enhanced configuration options for environment variables, network settings, and DNS record management.
  - Added support for managing ingress infrastructure within Kubernetes environments.

- **Chores**
  - Streamlined processes for floating IP allocation and removal.
  - Upgraded common infrastructure settings to improve resource provisioning and overall operational reliability.
  - Customized linting behavior by adding specific entries to ignore certain rules.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/.ansible-lint-ignore b/.ansible-lint-ignore
new file mode 100644
index 000000000..ca80a292b
--- /dev/null
+++ b/.ansible-lint-ignore
@@ -0,0 +1,2 @@
+playbook_cloudkit_delete_hosted_cluster.yml var-naming[no-role-prefix]
+playbook_cloudkit_create_hosted_cluster.yml var-naming[no-role-prefix]
diff --git a/.ansible-lint.yml b/.ansible-lint.yml
new file mode 100644
index 000000000..9be025c82
--- /dev/null
+++ b/.ansible-lint.yml
@@ -0,0 +1,2 @@
+skip_list:
+- role-name[path]
diff --git a/collections/requirements.yml b/collections/requirements.yml
new file mode 120000
index 000000000..39d6f8cbf
--- /dev/null
+++ b/collections/requirements.yml
@@ -0,0 +1 @@
+../execution-environment/requirements.yaml
\ No newline at end of file
diff --git a/playbook_cloudkit_create_hosted_cluster.yml b/playbook_cloudkit_create_hosted_cluster.yml
index ba770ef96..2c68a2cba 100644
--- a/playbook_cloudkit_create_hosted_cluster.yml
+++ b/playbook_cloudkit_create_hosted_cluster.yml
@@ -2,6 +2,8 @@
 - name: Create a hosted control plane cluster from a ClusterOrder
   hosts: localhost
   gather_facts: false
+  environment:
+    OS_CLOUD: "{{ openstack_cloud_name|default('devstack') }}"
 
   vars:
     cluster_order: "{{ ansible_eda.event.payload }}"
@@ -14,7 +16,7 @@
     - role: cluster_settings
       vars:
         cluster_settings_template_id: "{{ cluster_order.spec.templateID }}"
-        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters }}"
+        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters | from_json }}"
 
     - role: hosted_cluster
       vars:
@@ -22,3 +24,17 @@
         hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
         hosted_cluster_settings: "{{ cluster_settings_result }}"
         hosted_cluster_state: present
+
+    - role: infrastructure/api
+      vars:
+        hosted_cluster_name: "{{ cluster_order.metadata.name }}"
+        hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
+        hosted_cluster_settings: "{{ cluster_settings_result }}"
+        hosted_cluster_state: present
+
+    - role: infrastructure/ingress
+      vars:
+        hosted_cluster_name: "{{ cluster_order.metadata.name }}"
+        hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
+        hosted_cluster_settings: "{{ cluster_settings_result }}"
+        hosted_cluster_state: present
diff --git a/playbook_cloudkit_delete_hosted_cluster.yml b/playbook_cloudkit_delete_hosted_cluster.yml
index 74d91931f..56767dcbe 100644
--- a/playbook_cloudkit_delete_hosted_cluster.yml
+++ b/playbook_cloudkit_delete_hosted_cluster.yml
@@ -2,6 +2,8 @@
 - name: Teardown a hosted control plane cluster from a ClusterOrder
   hosts: localhost
   gather_facts: false
+  environment:
+    OS_CLOUD: "{{ openstack_cloud_name|default('devstack') }}"
 
   vars:
     cluster_order: "{{ ansible_eda.event.payload }}"
@@ -22,3 +24,17 @@
         hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
         hosted_cluster_settings: "{{ cluster_settings_result }}"
         hosted_cluster_state: absent
+
+    - role: infrastructure/api
+      vars:
+        hosted_cluster_name: "{{ cluster_order.metadata.name }}"
+        hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
+        hosted_cluster_settings: "{{ cluster_settings_result }}"
+        hosted_cluster_state: absent
+
+    - role: infrastructure/ingress
+      vars:
+        hosted_cluster_name: "{{ cluster_order.metadata.name }}"
+        hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
+        hosted_cluster_settings: "{{ cluster_settings_result }}"
+        hosted_cluster_state: absent
diff --git a/pyproject.toml b/pyproject.toml
index c3e9a6609..18cf5e75a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -6,9 +6,12 @@ readme = "README.md"
 requires-python = ">=3.13"
 dependencies = [
     "ansible>=11.4.0",
+    "boto3>=1.37.29",
+    "botocore>=1.37.29",
+    "jmespath>=1.0.1",
     "kubernetes>=32.0.1",
     "python-esiclient>=1.1.0",
     "python-esileapclient>=1.0.0",
     "python-ironicclient>=5.10.0",
-    "python-openstackclient>=6.0.1",
+    "python-openstackclient>=7.4.0",
 ]
diff --git a/roles/infrastructure/api/defaults/main.yaml b/roles/infrastructure/api/defaults/main.yaml
new file mode 100644
index 000000000..b05f552c3
--- /dev/null
+++ b/roles/infrastructure/api/defaults/main.yaml
@@ -0,0 +1,2 @@
+api_kube_apiserver_port: 6443         # noqa:var-naming[no-role-prefix]
+api_internal_ip_network: hypershift   # noqa:var-naming[no-role-prefix]
diff --git a/roles/infrastructure/api/meta/main.yaml b/roles/infrastructure/api/meta/main.yaml
new file mode 100644
index 000000000..ac4624948
--- /dev/null
+++ b/roles/infrastructure/api/meta/main.yaml
@@ -0,0 +1,2 @@
+dependencies:
+- role: infrastructure/common
diff --git a/roles/infrastructure/api/tasks/create_api_infra.yaml b/roles/infrastructure/api/tasks/create_api_infra.yaml
new file mode 100644
index 000000000..38160ad1f
--- /dev/null
+++ b/roles/infrastructure/api/tasks/create_api_infra.yaml
@@ -0,0 +1,54 @@
+- name: Get kube-apiserver service
+  kubernetes.core.k8s_info:
+    api_version: v1
+    kind: Service
+    namespace: "{{ hosted_cluster_namespace }}-{{ hosted_cluster_name }}"
+    name: kube-apiserver
+  register: kube_apiserver_service
+
+  # wait until the service exists and has a loadbalancer ip
+  until: >-
+    kube_apiserver_service.resources and
+    kube_apiserver_service.resources[0].status.loadBalancer.ingress | default(false)
+  retries: "{{ kube_apiserver_wait_retries | default(infrastructure_resource_wait_retries) }}"
+  delay: "{{ kube_apiserver_wait_delay | default(infrastructure_resource_wait_delay) }}"
+
+- name: Set api_internal_ip
+  ansible.builtin.set_fact:
+    api_internal_ip: "{{ kube_apiserver_service.resources[0].status.loadBalancer.ingress[0].ip }}"
+
+- name: Allocate api floating ip
+  ansible.builtin.include_role:
+    name: infrastructure
+    tasks_from: allocate_floating_ip
+  vars:
+    floating_ip_name: "{{ hosted_cluster_name }}-api"  # noqa:var-naming[no-role-prefix]
+
+- name: Set api_floating_ip
+  ansible.builtin.set_fact:
+    api_floating_ip: "{{ allocate_floating_ip_result }}"
+
+# FIXME: This will fail erroneously because the command is not idempotent;
+# see https://github.com/CCI-MOC/python-esiclient/issues/82. Unfortunately,
+# it will also fail legitimately if we failed to clean up from a previous
+# cluster and there exists a port forwarding to the same internal ip
+# address.
+- name: Create port forwarding  # noqa:no-changed-when
+  ansible.builtin.command: >-
+    openstack esi port forwarding create {{ api_internal_ip }} {{ api_floating_ip }}
+      --internal-ip-network "{{ api_internal_ip_network }}"
+      -p {{ api_kube_apiserver_port }} -d "{{ hosted_cluster_name }}" -f json
+  register: create_forwarding
+  failed_when: >-
+    create_forwarding.rc != 0 and "duplicate port forwarding" not in create_forwarding.stderr
+
+- name: Create dns record
+  amazon.aws.route53:
+    state: present
+    zone: "{{ hosted_cluster_settings.base_domain }}"
+    record: "api.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}"
+    type: A
+    ttl: "{{ infrastructure_dns_ttl }}"
+    value: "{{ api_floating_ip }}"
+    wait: true
+    overwrite: true
diff --git a/roles/infrastructure/api/tasks/destroy_api_infra.yaml b/roles/infrastructure/api/tasks/destroy_api_infra.yaml
new file mode 100644
index 000000000..f39f98850
--- /dev/null
+++ b/roles/infrastructure/api/tasks/destroy_api_infra.yaml
@@ -0,0 +1,25 @@
+- name: Wait for hostedcluster to finish deleting
+  kubernetes.core.k8s_info:
+    api_version: hypershift.openshift.io/v1beta1
+    kind: HostedCluster
+    namespace: "{{ hosted_cluster_namespace }}"
+    name: "{{ hosted_cluster_name }}"
+  register: hosted_cluster_check
+  until: not hosted_cluster_check.resources
+  retries: 360
+  delay: 5
+
+- name: Destroy api floating ip
+  ansible.builtin.include_role:
+    name: infrastructure
+    tasks_from: destroy_floating_ip.yaml
+  vars:
+    floating_ip_name: "{{ hosted_cluster_name }}-api"  # noqa:var-naming[no-role-prefix]
+
+- name: Delete api dns record
+  amazon.aws.route53:
+    state: absent
+    zone: "{{ hosted_cluster_settings.base_domain }}"
+    record: "api.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}"
+    type: A
+    wait: true
diff --git a/roles/infrastructure/api/tasks/main.yaml b/roles/infrastructure/api/tasks/main.yaml
new file mode 100644
index 000000000..cc88b699f
--- /dev/null
+++ b/roles/infrastructure/api/tasks/main.yaml
@@ -0,0 +1,7 @@
+- name: Create api infrastructure
+  when: hosted_cluster_state == "present"
+  ansible.builtin.include_tasks: create_api_infra.yaml
+
+- name: Destroy api infrastructure
+  when: hosted_cluster_state == "absent"
+  ansible.builtin.include_tasks: destroy_api_infra.yaml
diff --git a/roles/infrastructure/common/defaults/main.yaml b/roles/infrastructure/common/defaults/main.yaml
new file mode 100644
index 000000000..a3e625a82
--- /dev/null
+++ b/roles/infrastructure/common/defaults/main.yaml
@@ -0,0 +1,3 @@
+infrastructure_dns_ttl: 1800
+infrastructure_resource_wait_retries: 360
+infrastructure_resource_wait_delay: 5
diff --git a/roles/infrastructure/defaults/main.yaml b/roles/infrastructure/defaults/main.yaml
new file mode 100644
index 000000000..25a7b8cde
--- /dev/null
+++ b/roles/infrastructure/defaults/main.yaml
@@ -0,0 +1 @@
+infrastructure_floating_ip_network: external
diff --git a/roles/infrastructure/ingress/meta/main.yaml b/roles/infrastructure/ingress/meta/main.yaml
new file mode 100644
index 000000000..ac4624948
--- /dev/null
+++ b/roles/infrastructure/ingress/meta/main.yaml
@@ -0,0 +1,2 @@
+dependencies:
+- role: infrastructure/common
diff --git a/roles/infrastructure/ingress/tasks/create_ingress_infra.yaml b/roles/infrastructure/ingress/tasks/create_ingress_infra.yaml
new file mode 100644
index 000000000..7edcbbcd4
--- /dev/null
+++ b/roles/infrastructure/ingress/tasks/create_ingress_infra.yaml
@@ -0,0 +1,54 @@
+- name: Wait until agents are available
+  kubernetes.core.k8s_info:
+    api_version: agent-install.openshift.io/v1beta1
+    kind: Agent
+    namespace: "hardware-inventory"
+    label_selectors:
+    - "cloudkit.openshift.io/clusterorder={{ hosted_cluster_name }}"
+  register: agents
+
+  # wait until agents are assigned to the cluster
+  until: agents.resources
+  retries: "{{ agent_wait_retries | default(infrastructure_resource_wait_retries) }}"
+  delay: "{{ agent_wait_delay | default(infrastructure_resource_wait_delay) }}"
+
+- name: Set worker_node_ip
+  ansible.builtin.set_fact:
+    worker_node_ip: >-
+      {{
+      agents |
+      json_query('resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]') |
+      shuffle |
+      first |
+      ansible.utils.ipaddr('address')
+      }}
+
+- name: Allocate ingress floating ip
+  ansible.builtin.include_role:
+    name: infrastructure
+    tasks_from: allocate_floating_ip.yaml
+  vars:
+    floating_ip_name: "{{ hosted_cluster_name }}-ingress"  # noqa:var-naming[no-role-prefix]
+
+- name: Set ingress_floating_ip
+  ansible.builtin.set_fact:
+    ingress_floating_ip: "{{ allocate_floating_ip_result }}"
+
+- name: Create port forwarding for ingress  # noqa:no-changed-when
+  ansible.builtin.command: >-
+    openstack esi port forwarding create {{ worker_node_ip }} {{ ingress_floating_ip }}
+      -p 80 -p 443 -d "{{ hosted_cluster_name }}-ingress" -f json
+  register: create_forwarding
+  failed_when: >-
+    create_forwarding.rc != 0 and "duplicate port forwarding" not in create_forwarding.stderr
+
+- name: Create ingress dns record
+  amazon.aws.route53:
+    state: present
+    zone: "{{ hosted_cluster_settings.base_domain }}"
+    record: "*.apps.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}"
+    type: A
+    ttl: "{{ infrastructure_dns_ttl }}"
+    value: "{{ ingress_floating_ip }}"
+    wait: true
+    overwrite: true
diff --git a/roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml b/roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml
new file mode 100644
index 000000000..de3e7ce11
--- /dev/null
+++ b/roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml
@@ -0,0 +1,14 @@
+- name: Destroy ingress floating ip
+  ansible.builtin.include_role:
+    name: infrastructure
+    tasks_from: destroy_floating_ip.yaml
+  vars:
+    floating_ip_name: "{{ hosted_cluster_name }}-ingress"  # noqa:var-naming[no-role-prefix]
+
+- name: Delete ingress dns record
+  amazon.aws.route53:
+    state: absent
+    zone: "{{ hosted_cluster_settings.base_domain }}"
+    record: "*.apps.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}"
+    type: A
+    wait: true
diff --git a/roles/infrastructure/ingress/tasks/main.yaml b/roles/infrastructure/ingress/tasks/main.yaml
new file mode 100644
index 000000000..92c83394e
--- /dev/null
+++ b/roles/infrastructure/ingress/tasks/main.yaml
@@ -0,0 +1,7 @@
+- name: Create ingress infrastructure
+  when: hosted_cluster_state == "present"
+  ansible.builtin.include_tasks: create_ingress_infra.yaml
+
+- name: Destroy ingress infrastructure
+  when: hosted_cluster_state == "absent"
+  ansible.builtin.include_tasks: destroy_ingress_infra.yaml
diff --git a/roles/infrastructure/meta/main.yaml b/roles/infrastructure/meta/main.yaml
new file mode 100644
index 000000000..ac4624948
--- /dev/null
+++ b/roles/infrastructure/meta/main.yaml
@@ -0,0 +1,2 @@
+dependencies:
+- role: infrastructure/common
diff --git a/roles/infrastructure/tasks/allocate_floating_ip.yaml b/roles/infrastructure/tasks/allocate_floating_ip.yaml
new file mode 100644
index 000000000..527345b4d
--- /dev/null
+++ b/roles/infrastructure/tasks/allocate_floating_ip.yaml
@@ -0,0 +1,26 @@
+- name: Check if floating ip exists
+  openstack.cloud.floating_ip_info:
+    description: "{{ floating_ip_name }}"
+  register: fips
+
+- name: Set allocate_floating_ip_result to found ip
+  when: fips.floating_ips
+  ansible.builtin.set_fact:
+    allocate_floating_ip_result: "{{ fips.floating_ips[0].floating_ip_address }}"
+
+- name: Allocate a new floating ip
+  when: not fips.floating_ips
+  block:
+  - name: Allocate floating ip  # noqa:no-changed-when
+    ansible.builtin.command: >-
+      openstack floating ip create {{ infrastructure_floating_ip_network }} --description "{{ floating_ip_name }}"
+        -f json
+    register: fip_cmd_raw
+
+  - name: Unmarshal output from JSON
+    ansible.builtin.set_fact:
+      fip_cmd_result: "{{ fip_cmd_raw.stdout | from_json }}"
+
+  - name: Set allocate_floating_ip_result to new ip
+    ansible.builtin.set_fact:
+      allocate_floating_ip_result: "{{ fip_cmd_result.floating_ip_address }}"
diff --git a/roles/infrastructure/tasks/destroy_floating_ip.yaml b/roles/infrastructure/tasks/destroy_floating_ip.yaml
new file mode 100644
index 000000000..dbc278642
--- /dev/null
+++ b/roles/infrastructure/tasks/destroy_floating_ip.yaml
@@ -0,0 +1,23 @@
+- name: Look for floating ip
+  openstack.cloud.floating_ip_info:
+    description: "{{ floating_ip_name }}"
+  register: fips
+
+- name: Destroy floating floating_ip_address
+  when: fips.floating_ips
+  block:
+  - name: Extract ip address to destroy
+    ansible.builtin.set_fact:
+      destroy_floating_ip: "{{ fips.floating_ips[0].floating_ip_address }}"
+
+  - name: Remove port forwardings  # noqa:no-changed-when
+    ansible.builtin.command: >-
+      openstack esi port forwarding purge {{ destroy_floating_ip }} -f json
+
+  - name: Unset floating ip port  # noqa:no-changed-when
+    ansible.builtin.command: >-
+      openstack floating ip unset --port {{ destroy_floating_ip }}
+
+  - name: Delete floating ip  # noqa:no-changed-when
+    ansible.builtin.command: >-
+      openstack floating ip delete {{ destroy_floating_ip }}
diff --git a/uv.lock b/uv.lock
index d25db532d..7aed88f43 100644
--- a/uv.lock
+++ b/uv.lock
@@ -80,6 +80,34 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537 },
 ]
 
+[[package]]
+name = "boto3"
+version = "1.37.32"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "botocore" },
+    { name = "jmespath" },
+    { name = "s3transfer" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/56/7a/be6dfbe66f3a04434240edbb5425c0756f848af40c52194109afc0d265e9/boto3-1.37.32.tar.gz", hash = "sha256:bc08c95a88ffeb51d78d25cb8bd72593b8cce1d8fdcc650030aff98c15437d04", size = 111354 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/8a/3c/27d76e8e2400e129d5253837841849b8f66cbdda9fbdeae61c3d6713c6a6/boto3-1.37.32-py3-none-any.whl", hash = "sha256:6f0d3863abfeed366b365fb3ad2fa508d7f2becd64ca712d4b70b593da7f9763", size = 139561 },
+]
+
+[[package]]
+name = "botocore"
+version = "1.37.32"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "jmespath" },
+    { name = "python-dateutil" },
+    { name = "urllib3" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8f/68/407e8a712694eab28ca95ca2f9135d66e92975a54a37dda1aeefd26f8cd5/botocore-1.37.32.tar.gz", hash = "sha256:3e5d097690b3423adeefdf257384e964d0ba7f9575d77bf3f8998273b92ef700", size = 13815793 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/2d/7a/f0813da18b6f194e994666e0fd772a8cd8bc46b3c71e93994fdd8f6e573a/botocore-1.37.32-py3-none-any.whl", hash = "sha256:c25989e09e29b382c1edcc994f795c4faadf2f30470269754024a55269a7a47d", size = 13481235 },
+]
+
 [[package]]
 name = "cachetools"
 version = "5.5.2"
@@ -164,6 +192,9 @@ version = "0.1.0"
 source = { virtual = "." }
 dependencies = [
     { name = "ansible" },
+    { name = "boto3" },
+    { name = "botocore" },
+    { name = "jmespath" },
     { name = "kubernetes" },
     { name = "python-esiclient" },
     { name = "python-esileapclient" },
@@ -174,11 +205,14 @@ dependencies = [
 [package.metadata]
 requires-dist = [
     { name = "ansible", specifier = ">=11.4.0" },
+    { name = "boto3", specifier = ">=1.37.29" },
+    { name = "botocore", specifier = ">=1.37.29" },
+    { name = "jmespath", specifier = ">=1.0.1" },
     { name = "kubernetes", specifier = ">=32.0.1" },
     { name = "python-esiclient", specifier = ">=1.1.0" },
     { name = "python-esileapclient", specifier = ">=1.0.0" },
     { name = "python-ironicclient", specifier = ">=5.10.0" },
-    { name = "python-openstackclient", specifier = ">=6.0.1" },
+    { name = "python-openstackclient", specifier = ">=7.4.0" },
 ]
 
 [[package]]
@@ -546,10 +580,9 @@ wheels = [
 
 [[package]]
 name = "openstacksdk"
-version = "1.2.0"
+version = "4.5.0"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
-    { name = "appdirs" },
     { name = "cryptography" },
     { name = "decorator" },
     { name = "dogpile-cache" },
@@ -557,15 +590,17 @@ dependencies = [
     { name = "jmespath" },
     { name = "jsonpatch" },
     { name = "keystoneauth1" },
-    { name = "netifaces" },
     { name = "os-service-types" },
     { name = "pbr" },
+    { name = "platformdirs" },
+    { name = "psutil" },
     { name = "pyyaml" },
     { name = "requestsexceptions" },
+    { name = "typing-extensions" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/0a/7f/a3535e523ef1ae853229bb30af8f32d98f3fd21b50c0f42e917b6174afb2/openstacksdk-1.2.0.tar.gz", hash = "sha256:bc4b340fde80503b839bda72a32eb7d630ccde5c2c1ff4952840d79fca5bce7f", size = 1160015 }
+sdist = { url = "https://files.pythonhosted.org/packages/b8/72/0b7fe12a18f871dc57119651dba1a9c9976b8adcdfbad95b2e7224b06849/openstacksdk-4.5.0.tar.gz", hash = "sha256:ab7a1240207a6969ba09ceee8f16653805730677b4497a806cd956733651fe68", size = 1284921 }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/f3/62/0e53c59ea032cff8c7346dc6b94797a0eb5166ad65fb8f62fbc47c12d618/openstacksdk-1.2.0-py3-none-any.whl", hash = "sha256:d73b5b9825b14230d8faca0acc9dbbfacf1299eb8eceae1f0f6900ed17aede6e", size = 1626258 },
+    { url = "https://files.pythonhosted.org/packages/cb/f1/8c5ceee7e1f9519fb4dd1e33a8120c3b960dcdc8d7558ec92103df7c1ee2/openstacksdk-4.5.0-py3-none-any.whl", hash = "sha256:8ee80c594a2b710a3f4dc6a75334455bb8deb40f0bd42a706520210e82efc171", size = 1809217 },
 ]
 
 [[package]]
@@ -582,7 +617,7 @@ wheels = [
 
 [[package]]
 name = "osc-lib"
-version = "3.2.0"
+version = "4.0.0"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "cliff" },
@@ -594,9 +629,9 @@ dependencies = [
     { name = "requests" },
     { name = "stevedore" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/dc/db/0a907d200719f755f00579f116526abea404a8a7961d8cd4019f1e1235ed/osc-lib-3.2.0.tar.gz", hash = "sha256:5f706be145daf0e58068e3763ec56bde2f43ed229a738628e4c0fb1defb4ed9e", size = 99180 }
+sdist = { url = "https://files.pythonhosted.org/packages/47/84/ecd798d1aee59b5501de21e8e69f98f5ba275464053be62309a4d4a4b85b/osc_lib-4.0.0.tar.gz", hash = "sha256:1dd15dd64c2b62101487a0f774821839df6b2baa5abc1a572c8e6c53314ee3e7", size = 101874 }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/e4/cf/97bf2cfc86dfe15efdf238f49de3f918c677025bd4619b452f75ad5a1363/osc_lib-3.2.0-py3-none-any.whl", hash = "sha256:01c063f289c193a5e1867e5f557058ddd4222e6a56cefdc24ed5ed0a8a0951bd", size = 89257 },
+    { url = "https://files.pythonhosted.org/packages/6a/9f/db614f18e2a171cd4b2b2d660dcb5ed386ebe6caa6016b9a4f5746e211a1/osc_lib-4.0.0-py3-none-any.whl", hash = "sha256:7980be4452a6458f3eeb2f09d9b92f4dcc56a2f9c0b28513c99017158f89dfc1", size = 93121 },
 ]
 
 [[package]]
@@ -876,7 +911,7 @@ wheels = [
 
 [[package]]
 name = "python-esileapclient"
-version = "1.0.0"
+version = "1.1.0"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "esisdk" },
@@ -885,14 +920,14 @@ dependencies = [
     { name = "python-openstackclient" },
     { name = "six" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/81/99/2bb399819f805b3a5a3df24ff6752ce99967daff69f9c87bd61e9ef4d54b/python_esileapclient-1.0.0.tar.gz", hash = "sha256:caaab74d5d72749bad5b5d02ef259f9b2f3678284991a36fe06272d6f8787d79", size = 39812 }
+sdist = { url = "https://files.pythonhosted.org/packages/4d/cc/8b29dc822a10eb65320e63413e8e369858b8332294f8ed30c65d1f20470e/python_esileapclient-1.1.0.tar.gz", hash = "sha256:e192cd847827e68019ad1b33ead5b21702bf2a604be5b78cbde14810c5acf6f8", size = 40922 }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/78/ee/7eb240563825c43305dc5883bd684d6df68f34cc2e0ddbd834252c371a02/python_esileapclient-1.0.0-py3-none-any.whl", hash = "sha256:fb581c982bfe9f0824e3703eabbd80284f4ebbf94f6a83183358d81c7590c3f7", size = 63361 },
+    { url = "https://files.pythonhosted.org/packages/af/34/4b70ec908209f4fae553c648fd2a1f0f8e455a4e5828234ff47d21b461e6/python_esileapclient-1.1.0-py3-none-any.whl", hash = "sha256:eba10e1cf009356a883da5b47e9d46ce51142499d5e57d28f84245a97600645e", size = 63144 },
 ]
 
 [[package]]
 name = "python-ironicclient"
-version = "5.10.0"
+version = "5.10.1"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "cliff" },
@@ -908,9 +943,9 @@ dependencies = [
     { name = "requests" },
     { name = "stevedore" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/c9/be/c8fa91c8c3554eceff66ba998af9f8b90b94c467353e251c9c05618f4eb7/python-ironicclient-5.10.0.tar.gz", hash = "sha256:120d9e6a4ae2e7ed613ee2b2abdf9fe1d2f7de297a1b82d82d9be85987d4e0b6", size = 222766 }
+sdist = { url = "https://files.pythonhosted.org/packages/a1/06/740598bc5f2d9c3aad5956e2efb3c623bed06434a7494a5a87f20807a6bf/python_ironicclient-5.10.1.tar.gz", hash = "sha256:d263411bf590eda7d51ed8c515333f2092d4e13032a588acdaadbd11db5d05f7", size = 223033 }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/7b/72/fac8eb2490312925fe26b49ac3b93372d1d140fe5bd8dc09b79a5576958f/python_ironicclient-5.10.0-py3-none-any.whl", hash = "sha256:4fe07f4c9a4c5f22c423f38a0d2284fb023a41f673470810e71f3ac279b9b9de", size = 240973 },
+    { url = "https://files.pythonhosted.org/packages/85/50/4c58bdd2c2a0536cc8a772221ce41de8f6e67c13e0d1a5c44ca3298ed564/python_ironicclient-5.10.1-py3-none-any.whl", hash = "sha256:662cfd05c91a1aec5d93705b6aa1a0b204cc4cd24c3c180dc311985844837932", size = 240943 },
 ]
 
 [[package]]
@@ -955,24 +990,24 @@ wheels = [
 
 [[package]]
 name = "python-openstackclient"
-version = "6.0.1"
+version = "8.0.0"
 source = { registry = "https://pypi.org/simple" }
 dependencies = [
     { name = "cliff" },
+    { name = "cryptography" },
     { name = "iso8601" },
     { name = "openstacksdk" },
     { name = "osc-lib" },
     { name = "oslo-i18n" },
-    { name = "oslo-utils" },
     { name = "pbr" },
     { name = "python-cinderclient" },
     { name = "python-keystoneclient" },
-    { name = "python-novaclient" },
+    { name = "requests" },
     { name = "stevedore" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/1a/a5/ed0dcfa4dcc2effb0d9963d650d8923407544296713aa45447469124553b/python-openstackclient-6.0.1.tar.gz", hash = "sha256:0b9d9e856d906d4ed6cd21cf80b2716c85b7e7b01e35840f0c4af212ca04cda2", size = 839450 }
+sdist = { url = "https://files.pythonhosted.org/packages/81/81/afb257489a665cfc330129cc8bb74c4131a085c310eeb74cc86391c3dd06/python_openstackclient-8.0.0.tar.gz", hash = "sha256:5b7a5e06893f833b5d296d019c50d42c7368e37748ee6be8e9b15655b999424e", size = 914450 }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/2a/8d/8b9a2fd630f1e2798f0690fdc4aaf58aac5c8dd2934d77b3c2d58a344ce6/python_openstackclient-6.0.1-py3-none-any.whl", hash = "sha256:08574fcb61e6cdd6ced276faa07ea9faea9c06fa5d987a9ba48cdac3dc63f1df", size = 999571 },
+    { url = "https://files.pythonhosted.org/packages/23/55/a3df5e4e0fdee95b4375100b00dd834c65d9ff7b37586324c4643cfcbfd5/python_openstackclient-8.0.0-py3-none-any.whl", hash = "sha256:b4c0c8436679d250a30d170446661d783964039d8fc9e6c9e3194ed321fd5944", size = 1066118 },
 ]
 
 [[package]]
@@ -1106,6 +1141,18 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/49/97/fa78e3d2f65c02c8e1268b9aba606569fe97f6c8f7c2d74394553347c145/rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7", size = 34315 },
 ]
 
+[[package]]
+name = "s3transfer"
+version = "0.11.4"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "botocore" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/0f/ec/aa1a215e5c126fe5decbee2e107468f51d9ce190b9763cb649f76bb45938/s3transfer-0.11.4.tar.gz", hash = "sha256:559f161658e1cf0a911f45940552c696735f5c74e64362e515f333ebed87d679", size = 148419 }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/86/62/8d3fc3ec6640161a5649b2cddbbf2b9fa39c92541225b33f117c37c5a2eb/s3transfer-0.11.4-py3-none-any.whl", hash = "sha256:ac265fa68318763a03bf2dc4f39d5cbd6a9e178d81cc9483ad27da33637e320d", size = 84412 },
+]
+
 [[package]]
 name = "setuptools"
 version = "78.1.0"
@@ -1160,11 +1207,11 @@ wheels = [
 
 [[package]]
 name = "typing-extensions"
-version = "4.13.1"
+version = "4.13.2"
 source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/76/ad/cd3e3465232ec2416ae9b983f27b9e94dc8171d56ac99b345319a9475967/typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff", size = 106633 }
+sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/df/c5/e7a0b0f5ed69f94c8ab7379c599e6036886bffcde609969a5325f47f1332/typing_extensions-4.13.1-py3-none-any.whl", hash = "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69", size = 45739 },
+    { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 },
 ]
 
 [[package]]
@@ -1178,11 +1225,11 @@ wheels = [
 
 [[package]]
 name = "urllib3"
-version = "2.3.0"
+version = "2.4.0"
 source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 }
+sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672 }
 wheels = [
-    { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 },
+    { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680 },
 ]
 
 [[package]]


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 7**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>roles/infrastructure/api/tasks/main.yaml (1)</summary><blockquote>

`1-6`: _🛠️ Refactor suggestion_

**Task Inclusion Using Relative Paths**:  
The usage of `include_tasks` with direct file names (e.g., `create_api_infra.yaml` and `destroy_api_infra.yaml`) is triggering a pipeline warning regarding paths when importing roles. To improve portability and adhere to best practices, consider using `import_tasks` with an absolute reference using the `{{ role_path }}` variable. For example:

```diff
-  include_tasks: create_api_infra.yaml
+  import_tasks: "{{ role_path }}/tasks/create_api_infra.yaml"
...
-  include_tasks: destroy_api_infra.yaml
+  import_tasks: "{{ role_path }}/tasks/destroy_api_infra.yaml"
```

This change will ensure that task inclusions are correctly resolved relative to the role’s directory.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[error] 1-1: Avoid using paths when importing roles. (infrastructure/api)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (11)</summary><blockquote>

<details>
<summary>roles/infrastructure/tasks/destroy_floating_ip.yaml (1)</summary><blockquote>

`9-10`: **Improve Jinja2 Spacing in set_fact.**  
The Jinja2 expression in the set_fact task should use consistent spacing. Consider updating  
```yaml
destroy_floating_ip: "{{fips.floating_ips[0].floating_ip_address}}"
```  
to  
```yaml
destroy_floating_ip: "{{ fips.floating_ips[0].floating_ip_address }}"
```  
to improve readability.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 10-10: jinja[spacing]
Jinja2 spacing could be improved: {{fips.floating_ips[0].floating_ip_address}} -> {{ fips.floating_ips[0].floating_ip_address }}

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml (1)</summary><blockquote>

`12-12`: **Improve Jinja2 Spacing in DNS Record Value.**  
The DNS record entry currently is:  
```yaml
record: "*.apps.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}}"
```  
For better clarity, adjust the spacing as follows:  
```yaml
record: "*.apps.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: pre-commit</summary>

[error] 12-12: pre-commit hook 'end-of-file-fixer' failed. Trailing whitespace was removed.

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/api/tasks/destroy_api_infra.yaml (1)</summary><blockquote>

`19-23`: **Standardize DNS Record Task Naming and Jinja2 Formatting.**  
1. The task name "delete api dns record" should start with an uppercase letter, for instance: "Delete API DNS Record".  
2. The DNS record value can be improved for consistent spacing. Replace:  
```yaml
record: "api.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}}"
```  
with  
```yaml
record: "api.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}"
```  
to align with the guidelines.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 21-21: jinja[spacing]
Jinja2 spacing could be improved: api.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}} -> api.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}

</details>
<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 19-19: All names should start with an uppercase letter.

---

[warning] 21-21: Jinja2 spacing could be improved: api.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}} -> api.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }} (warning)

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/tasks/allocate_floating_ip.yaml (2)</summary><blockquote>

`8-9`: **Improve Jinja2 Spacing in set_fact for Allocated IP.**  
Update the set_fact expression to use consistent spacing. Change:  
```yaml
allocate_floating_ip_result: "{{fips.floating_ips[0].floating_ip_address}}"
```  
to  
```yaml
allocate_floating_ip_result: "{{ fips.floating_ips[0].floating_ip_address }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 9-9: jinja[spacing]
Jinja2 spacing could be improved: {{fips.floating_ips[0].floating_ip_address}} -> {{ fips.floating_ips[0].floating_ip_address }}

</details>

</details>

---

`19-20`: **Enhance Jinja2 Formatting for JSON Parsing.**  
For readability and consistency, modify the JSON parsing line from:  
```yaml
fip_cmd_result: "{{ fip_cmd_raw.stdout|from_json }}"
```  
to  
```yaml
fip_cmd_result: "{{ fip_cmd_raw.stdout | from_json }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 20-20: jinja[spacing]
Jinja2 spacing could be improved: {{ fip_cmd_raw.stdout|from_json }} -> {{ fip_cmd_raw.stdout | from_json }}

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/api/tasks/create_api_infra.yaml (3)</summary><blockquote>

`10-14`: **Wait Mechanism for Service Readiness**

The loop using `until` and checking that the service’s load balancer has an ingress IP is functionally sound. A minor nitpick: consider improving Jinja2 spacing (e.g. add spaces around the filter `| default`) for readability.

---

`16-19`: **Setting API Internal IP**

The task sets the `api_internal_ip` fact based on the service data. For better adherence to best practices, consider using the fully qualified collection name for the `set_fact` module (i.e. `ansible.builtin.set_fact`). 


```diff
-  set_fact:
-    api_internal_ip: "{{ kube_apiserver_service.resources[0].status.loadBalancer.ingress[0].ip }}"
+  ansible.builtin.set_fact:
+    api_internal_ip: "{{ kube_apiserver_service.resources[0].status.loadBalancer.ingress[0].ip }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 16-16: Use FQCN for builtin module actions (set_fact).

---

[warning] 16-16: All names should start with an uppercase letter.

</details>

</details>

---

`27-30`: **Setting API Floating IP**

The fact is set for the API floating IP using the result from the previous step. As with the earlier `set_fact` usage, using the FQCN (e.g., `ansible.builtin.set_fact`) is recommended to meet best practices.


```diff
-  set_fact:
-    api_floating_ip: "{{ allocate_floating_ip_result }}"
+  ansible.builtin.set_fact:
+    api_floating_ip: "{{ allocate_floating_ip_result }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 27-27: Use FQCN for builtin module actions (set_fact).

---

[warning] 27-27: All names should start with an uppercase letter.

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/ingress/tasks/create_ingress_infra.yaml (3)</summary><blockquote>

`10-14`: **Agent Assignment Wait Loop**

The wait loop ensuring that agents are assigned to the cluster is correctly implemented. Like earlier, minor formatting improvements for Jinja2 spacing (e.g., spaces around the `| default` filter) could enhance readability.

---

`15-25`: **Setting Worker Node IP**

The `set_fact` task uses a compound Jinja2 expression to extract, shuffle, and select the worker node IP from the agents’ data. Functionally, this approach is effective; however, consider reformatting the expression with improved spacing for clarity as suggested by the static analysis tools.


```diff
-    worker_node_ip: >-
-      {{
-      agents|
-      json_query('resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]')|
-      shuffle|
-      first|
-      ansible.utils.ipaddr('address')
-      }}
+    worker_node_ip: >-
+      {{ agents | json_query("resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]") | shuffle | first | ansible.utils.ipaddr("address") }}
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 17-17: jinja[spacing]
Jinja2 spacing could be improved: {{ agents| json_query('resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]')| shuffle| first| ansible.utils.ipaddr('address') }} -> {{ agents | json_query('resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]') | shuffle | first | ansible.utils.ipaddr('address') }}

</details>

</details>

---

`33-36`: **Setting Ingress Floating IP**

The task sets the fact `ingress_floating_ip` using the allocation result, which is appropriate. As before, consider using the fully qualified module name (`ansible.builtin.set_fact`) for consistency.


```diff
-  set_fact:
-    ingress_floating_ip: "{{ allocate_floating_ip_result }}"
+  ansible.builtin.set_fact:
+    ingress_floating_ip: "{{ allocate_floating_ip_result }}"
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 22a9fdd4f05b24f2b62bc4065945a5c35e69d56d and 82830aca3acc01a564476372d6d8de076ccf3000.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `uv.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (18)</summary>

* `collections/requirements.yml` (1 hunks)
* `playbook_cloudkit_create_hosted_cluster.yml` (2 hunks)
* `playbook_cloudkit_delete_hosted_cluster.yml` (2 hunks)
* `pyproject.toml` (1 hunks)
* `roles/infrastructure/api/defaults/main.yaml` (1 hunks)
* `roles/infrastructure/api/meta/main.yaml` (1 hunks)
* `roles/infrastructure/api/tasks/create_api_infra.yaml` (1 hunks)
* `roles/infrastructure/api/tasks/destroy_api_infra.yaml` (1 hunks)
* `roles/infrastructure/api/tasks/main.yaml` (1 hunks)
* `roles/infrastructure/common/defaults/main.yaml` (1 hunks)
* `roles/infrastructure/defaults/main.yaml` (1 hunks)
* `roles/infrastructure/ingress/meta/main.yaml` (1 hunks)
* `roles/infrastructure/ingress/tasks/create_ingress_infra.yaml` (1 hunks)
* `roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml` (1 hunks)
* `roles/infrastructure/ingress/tasks/main.yaml` (1 hunks)
* `roles/infrastructure/meta/main.yaml` (1 hunks)
* `roles/infrastructure/tasks/allocate_floating_ip.yaml` (1 hunks)
* `roles/infrastructure/tasks/destroy_floating_ip.yaml` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

<details>
<summary>roles/infrastructure/api/meta/main.yaml</summary>

[warning] 2-2: Avoid using paths when importing roles. (infrastructure/common)

</details>
<details>
<summary>roles/infrastructure/api/defaults/main.yaml</summary>

[warning] 1-1: Variables names from within roles should use infrastructure_ as a prefix. (vars: kube_apiserver_port)

---

[warning] 2-2: Variables names from within roles should use infrastructure_ as a prefix. (vars: internal_ip_network)

</details>
<details>
<summary>roles/infrastructure/api/tasks/main.yaml</summary>

[error] 1-1: Avoid using paths when importing roles. (infrastructure/api)

</details>
<details>
<summary>playbook_cloudkit_delete_hosted_cluster.yml</summary>

[warning] 28-28: Avoid using paths when importing roles. (infrastructure/api)

---

[warning] 30-30: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 31-31: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 32-32: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 33-33: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

---

[warning] 35-35: Avoid using paths when importing roles. (infrastructure/ingress)

---

[warning] 37-37: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 38-38: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 39-39: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 40-40: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

</details>
<details>
<summary>roles/infrastructure/api/tasks/create_api_infra.yaml</summary>

[warning] 1-1: Jinja2 spacing could be improved: {{ kube_apiserver_wait_delay|default(resource_wait_delay) }} -> {{ kube_apiserver_wait_delay | default(resource_wait_delay) }} (warning)

---

[warning] 1-1: Jinja2 spacing could be improved: {{ kube_apiserver_wait_retries|default(resource_wait_retries) }} -> {{ kube_apiserver_wait_retries | default(resource_wait_retries) }} (warning)

---

[warning] 1-1: All names should start with an uppercase letter.

---

[warning] 16-16: Use FQCN for builtin module actions (set_fact).

---

[warning] 16-16: All names should start with an uppercase letter.

---

[warning] 20-20: Use FQCN for builtin module actions (include_role).

---

[warning] 20-20: All names should start with an uppercase letter.

---

[warning] 25-25: Variables names from within roles should use infrastructure_ as a prefix. (vars: floating_ip_name)

---

[warning] 27-27: Use FQCN for builtin module actions (set_fact).

---

[warning] 27-27: All names should start with an uppercase letter.

---

[warning] 36-36: Use FQCN for builtin module actions (command).

---

[warning] 36-36: All names should start with an uppercase letter.

---

[warning] 45-45: All names should start with an uppercase letter.

</details>
<details>
<summary>roles/infrastructure/api/tasks/destroy_api_infra.yaml</summary>

[warning] 1-1: All tasks should be named.

---

[warning] 12-12: Use FQCN for builtin module actions (include_role).

---

[warning] 17-17: Variables names from within roles should use infrastructure_ as a prefix. (vars: floating_ip_name)

---

[warning] 19-19: All names should start with an uppercase letter.

---

[warning] 21-21: Jinja2 spacing could be improved: api.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}} -> api.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }} (warning)

</details>
<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml</summary>

[warning] 28-28: Avoid using paths when importing roles. (infrastructure/api)

---

[warning] 30-30: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 31-31: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 32-32: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 33-33: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

---

[warning] 35-35: Avoid using paths when importing roles. (infrastructure/ingress)

---

[warning] 37-37: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 38-38: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 39-39: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 40-40: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

</details>

</details>
<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

<details>
<summary>roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml</summary>

[warning] 10-10: jinja[spacing]
Jinja2 spacing could be improved: *.apps.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}} -> *.apps.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}

</details>
<details>
<summary>roles/infrastructure/tasks/allocate_floating_ip.yaml</summary>

[warning] 20-20: jinja[spacing]
Jinja2 spacing could be improved: {{ fip_cmd_raw.stdout|from_json }} -> {{ fip_cmd_raw.stdout | from_json }}

---

[warning] 9-9: jinja[spacing]
Jinja2 spacing could be improved: {{fips.floating_ips[0].floating_ip_address}} -> {{ fips.floating_ips[0].floating_ip_address }}

</details>
<details>
<summary>roles/infrastructure/tasks/destroy_floating_ip.yaml</summary>

[warning] 10-10: jinja[spacing]
Jinja2 spacing could be improved: {{fips.floating_ips[0].floating_ip_address}} -> {{ fips.floating_ips[0].floating_ip_address }}

</details>
<details>
<summary>roles/infrastructure/api/tasks/create_api_infra.yaml</summary>

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ kube_apiserver_wait_retries|default(resource_wait_retries) }} -> {{ kube_apiserver_wait_retries | default(resource_wait_retries) }}

---

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ kube_apiserver_wait_delay|default(resource_wait_delay) }} -> {{ kube_apiserver_wait_delay | default(resource_wait_delay) }}

</details>
<details>
<summary>roles/infrastructure/ingress/tasks/create_ingress_infra.yaml</summary>

[warning] 17-17: jinja[spacing]
Jinja2 spacing could be improved: {{ agents| json_query('resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]')| shuffle| first| ansible.utils.ipaddr('address') }} -> {{ agents | json_query('resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]') | shuffle | first | ansible.utils.ipaddr('address') }}

---

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ agent_wait_retries|default(resource_wait_retries) }} -> {{ agent_wait_retries | default(resource_wait_retries) }}

---

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ agent_wait_delay|default(resource_wait_delay) }} -> {{ agent_wait_delay | default(resource_wait_delay) }}

</details>
<details>
<summary>roles/infrastructure/api/tasks/destroy_api_infra.yaml</summary>

[warning] 21-21: jinja[spacing]
Jinja2 spacing could be improved: api.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}} -> api.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}

</details>
<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml</summary>

[failure] 40-40: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

---

[failure] 39-39: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[failure] 38-38: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[failure] 37-37: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[failure] 35-35: role-name[path]
Avoid using paths when importing roles. (infrastructure/ingress)

---

[failure] 33-33: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

---

[failure] 32-32: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[failure] 31-31: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[failure] 30-30: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

---

[failure] 28-28: role-name[path]
Avoid using paths when importing roles. (infrastructure/api)

</details>

</details>
<details>
<summary>🪛 GitHub Actions: pre-commit</summary>

<details>
<summary>roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml</summary>

[error] 12-12: pre-commit hook 'end-of-file-fixer' failed. Trailing whitespace was removed.

</details>
<details>
<summary>roles/infrastructure/tasks/allocate_floating_ip.yaml</summary>

[error] 22-22: pre-commit hook 'end-of-file-fixer' failed. Trailing whitespace was removed.

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (19)</summary><blockquote>

<details>
<summary>roles/infrastructure/defaults/main.yaml (1)</summary>

`1-2`: **Configuration for Floating IP Network Set Correctly**  
The new default value for `floating_ip_network` as `external` appears straightforward and in line with establishing the appropriate network for floating IP allocations.

</details>
<details>
<summary>roles/infrastructure/meta/main.yaml (1)</summary>

`1-3`: **Dependency Declaration on Common Infrastructure Role**  
The dependency on `infrastructure/common` is declared correctly. However, note the CI warning regarding "Avoid using paths when importing roles." Please verify that this dependency reference is interpreted as a role name rather than a filesystem path to remain consistent with Ansible best practices.

</details>
<details>
<summary>roles/infrastructure/api/meta/main.yaml (1)</summary>

`1-3`: **Ensure Consistent Role Dependency Reference in API Role**  
The dependency on `infrastructure/common` is set correctly here as well. Given the CI warning about importing roles via paths, please verify that this reference conforms to the expected role naming conventions and does not inadvertently introduce path-based imports.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 2-2: Avoid using paths when importing roles. (infrastructure/common)

</details>

</details>

</details>
<details>
<summary>roles/infrastructure/ingress/tasks/main.yaml (1)</summary>

`1-6`: **Conditional Task Inclusion for Ingress Infrastructure**  
The conditional inclusion of `create_ingress_infra.yaml` and `destroy_ingress_infra.yaml` based on the `hosted_cluster_state` variable looks clear and correctly structured. Double-check that the mutually exclusive conditions (`present` vs. `absent`) cover all expected states, and that the indentation meets Ansible’s YAML format requirements.

</details>
<details>
<summary>roles/infrastructure/ingress/meta/main.yaml (1)</summary>

`1-3`: **Ingress Role Dependency on Common Infrastructure**  
The dependency declaration on `infrastructure/common` is consistent with the other roles. As with the other meta files, please verify that this does not trigger path import issues per the CI warning. Confirm that using `infrastructure/common` in this context is acceptable per the project’s role-loading practices.

</details>
<details>
<summary>roles/infrastructure/common/defaults/main.yaml (1)</summary>

`1-3`: **LGTM!**  

The new default configurations (`dns_ttl`, `resource_wait_retries`, and `resource_wait_delay`) are well-defined and clear.

</details>
<details>
<summary>collections/requirements.yml (1)</summary>

`5-12`: **Collections Additions Look Good!**  

The new collection entries for `openstack.cloud`, `amazon.aws`, `community.general`, and `ansible.utils` are correctly formatted and align with the integration requirements for the new roles.

</details>
<details>
<summary>pyproject.toml (1)</summary>

`6-15`: **Dependency and Python Version Check**:  

The additional dependencies for AWS and OpenStack support (`boto3`, `botocore`, `python-esiclient`, `python-esileapclient`, and `python-openstackclient`) are appropriate given the new integration roles. However, please verify that the Python version requirement (`>=3.13`) is intentional, as this may restrict the project to very recent Python releases.

</details>
<details>
<summary>playbook_cloudkit_delete_hosted_cluster.yml (1)</summary>

`5-6`: **Set OS_CLOUD environment variable correctly.**  
The playbook now injects the OS_CLOUD variable with a default value (derived from `openstack_cloud_name`), which promotes consistency across environments.

</details>
<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml (4)</summary>

`5-6`: **Environment Variable Addition Verification**

The playbook now sets the `OS_CLOUD` environment variable (defaulting to `"devstack"`) for cluster creation. This is a helpful addition for OpenStack integration. Please verify that this default value is appropriate for all deployment scenarios and that its usage is consistent with the delete playbook.

---

`19-19`: **Enhanced JSON Parameter Parsing**

The change for parsing `cluster_order.spec.templateParameters` with the `from_json` filter improves the robustness of parameter handling. Ensure that the incoming data is always a valid JSON string to avoid runtime errors.

---

`28-33`: <details>
<summary>❓ Verification inconclusive</summary>

**API Role Reference and Variable Naming Consistency**

The playbook adds the API infrastructure role using a path (`infrastructure/api`) and passes variables with the `hosted_cluster_` prefix. According to our guidelines and the static analysis warnings, it’s recommended to avoid path-based role references and to adopt a role-specific prefix (e.g. `api_`). This helps with clarity and role encapsulation.



```diff
-    - role: infrastructure/api
-      vars:
-        hosted_cluster_name: "{{ cluster_order.metadata.name }}"
-        hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
-        hosted_cluster_settings: "{{ cluster_settings_result }}"
-        hosted_cluster_state: present
+    - role: api_infrastructure
+      vars:
+        api_cluster_name: "{{ cluster_order.metadata.name }}"
+        api_cluster_namespace: "{{ cluster_working_namespace_result }}"
+        api_cluster_settings: "{{ cluster_settings_result }}"
+        api_cluster_state: present
```

---

Based on the guidelines and our static analysis warnings, the current playbook uses a path-based role reference (`infrastructure/api`) and variables prefixed with `hosted_cluster_`. We recommend updating the role reference to a standardized name (e.g. `api_infrastructure`) and renaming the corresponding variables to use the `api_` prefix to promote clarity and maintain consistency.

**API Role and Variable Naming Update Verification**

- Avoid using path-based role references like `infrastructure/api`; instead, switch to a role-specific name, e.g. `api_infrastructure`.
- Update variable names from `hosted_cluster_*` to `api_cluster_*` so that the API role’s scope is clearer.
- Verify that these changes align with the role’s implementation and dependencies in the codebase.

```diff
-    - role: infrastructure/api
-      vars:
-        hosted_cluster_name: "{{ cluster_order.metadata.name }}"
-        hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
-        hosted_cluster_settings: "{{ cluster_settings_result }}"
-        hosted_cluster_state: present
+    - role: api_infrastructure
+      vars:
+        api_cluster_name: "{{ cluster_order.metadata.name }}"
+        api_cluster_namespace: "{{ cluster_working_namespace_result }}"
+        api_cluster_settings: "{{ cluster_settings_result }}"
+        api_cluster_state: present
```

Please confirm that the role implementation and any related tasks are updated accordingly to align with these changes.



---

🏁 Script executed:

```shell
#!/bin/bash
# Verify occurrences of the problematic role reference and variable naming in the playbook file.
rg -n "infrastructure/api" playbook_cloudkit_create_hosted_cluster.yml
```

Length of output: 104

---



</details>

**Refactor Role Reference and Variable Naming**

The playbook currently uses a path-based role reference (`infrastructure/api`) and variables with a `hosted_cluster_` prefix, which conflicts with our guidelines. To improve clarity and maintain role encapsulation, please update the playbook as follows:

- Change the role reference from `infrastructure/api` to a standardized role name like `api_infrastructure`.
- Update variable names from the `hosted_cluster_` prefix to `api_cluster_` to align with the role’s context.

```diff
-    - role: infrastructure/api
-      vars:
-        hosted_cluster_name: "{{ cluster_order.metadata.name }}"
-        hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
-        hosted_cluster_settings: "{{ cluster_settings_result }}"
-        hosted_cluster_state: present
+    - role: api_infrastructure
+      vars:
+        api_cluster_name: "{{ cluster_order.metadata.name }}"
+        api_cluster_namespace: "{{ cluster_working_namespace_result }}"
+        api_cluster_settings: "{{ cluster_settings_result }}"
+        api_cluster_state: present
```

Please ensure that the role implementation and any dependent tasks are updated accordingly.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[failure] 33-33: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

---

[failure] 32-32: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[failure] 31-31: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[failure] 30-30: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

---

[failure] 28-28: role-name[path]
Avoid using paths when importing roles. (infrastructure/api)

</details>
<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 28-28: Avoid using paths when importing roles. (infrastructure/api)

---

[warning] 30-30: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 31-31: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 32-32: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 33-33: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

</details>

</details>

---

`35-40`: <details>
<summary>❌ Incorrect review comment</summary>

**Ingress Role Reference and Variable Naming Consistency**

Similarly, the ingress role is included via a path (`infrastructure/ingress`) and uses variables with the `hosted_cluster_` prefix. For consistency and to adhere to the naming standard, consider changing the role reference (e.g. to `ingress_infrastructure`) and renaming the passed variables with an `ingress_` prefix.



```diff
-    - role: infrastructure/ingress
-      vars:
-        hosted_cluster_name: "{{ cluster_order.metadata.name }}"
-        hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
-        hosted_cluster_settings: "{{ cluster_settings_result }}"
-        hosted_cluster_state: present
+    - role: ingress_infrastructure
+      vars:
+        ingress_cluster_name: "{{ cluster_order.metadata.name }}"
+        ingress_cluster_namespace: "{{ cluster_working_namespace_result }}"
+        ingress_cluster_settings: "{{ cluster_settings_result }}"
+        ingress_cluster_state: present
```

---

Below are two shell scripts to verify the concerns raised in the review comment. The first script searches for all occurrences of the original role reference (`infrastructure/ingress`) to understand its usage across the codebase. The second script looks for variable occurrences using the `hosted_cluster_` prefix. Once these scripts are run and you review their outputs, we can determine whether the proposed renaming to `ingress_infrastructure` and the variable prefix change to `ingress_` are consistent with the overall codebase.





Once you run these scripts and confirm that the role and variables are consistently used in this way, we can confidently proceed with the renaming for consistency. 



---

🏁 Script executed:

```shell

#!/bin/bash
# Verify the usage of the role reference "infrastructure/ingress"
rg "role:\\s*infrastructure/ingress" -n .
```

Length of output: 332

---

🏁 Script executed:

```shell

#!/bin/bash
# Verify variables using the "hosted_cluster_" prefix throughout the repository
rg "hosted_cluster_" -n .
```

Length of output: 8958

---



</details>

**Role and Variable Naming Consistency Assessment**

After reviewing the codebase, it’s apparent that the role is consistently referenced as `infrastructure/ingress` and that the variables use the `hosted_cluster_` prefix across multiple playbooks and role tasks (as seen in both `playbook_cloudkit_create_hosted_cluster.yml` and `playbook_cloudkit_delete_hosted_cluster.yml`). Changing the role name to `ingress_infrastructure` and renaming the variables to use an `ingress_` prefix in only one playbook would break the existing naming consistency. 

Please consider one of the following approaches:
- If a renaming is desired for clarity, perform a comprehensive refactor across all relevant files (including both playbooks and all related role tasks) to update both the role reference and the variable naming consistently.
- Otherwise, retain the current terminology to maintain uniformity throughout the codebase.



> Likely an incorrect or invalid review comment.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[failure] 40-40: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

---

[failure] 39-39: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[failure] 38-38: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[failure] 37-37: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[failure] 35-35: role-name[path]
Avoid using paths when importing roles. (infrastructure/ingress)

</details>
<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 35-35: Avoid using paths when importing roles. (infrastructure/ingress)

---

[warning] 37-37: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 38-38: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 39-39: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 40-40: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

</details>

</details>

</details>
<details>
<summary>roles/infrastructure/api/tasks/create_api_infra.yaml (3)</summary>

`1-7`: **Retrieve kube-apiserver Service**

The task correctly uses the `kubernetes.core.k8s_info` module to fetch the kube-apiserver service. Note that the namespace is constructed as `"{{ hosted_cluster_namespace }}-{{ hosted_cluster_name }}"`; please confirm this concatenated value meets your naming conventions.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ kube_apiserver_wait_retries|default(resource_wait_retries) }} -> {{ kube_apiserver_wait_retries | default(resource_wait_retries) }}

---

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ kube_apiserver_wait_delay|default(resource_wait_delay) }} -> {{ kube_apiserver_wait_delay | default(resource_wait_delay) }}

</details>
<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 1-1: Jinja2 spacing could be improved: {{ kube_apiserver_wait_delay|default(resource_wait_delay) }} -> {{ kube_apiserver_wait_delay | default(resource_wait_delay) }} (warning)

---

[warning] 1-1: Jinja2 spacing could be improved: {{ kube_apiserver_wait_retries|default(resource_wait_retries) }} -> {{ kube_apiserver_wait_retries | default(resource_wait_retries) }} (warning)

---

[warning] 1-1: All names should start with an uppercase letter.

</details>

</details>

---

`31-44`: **API Port Forwarding Command**

The command to create port forwarding via the OpenStack ESI client is comprehensive. The use of a conditional `failed_when` clause to ignore duplicate port forwarding errors is appropriate given the known idempotency issue. Please double-check that ignoring duplicates aligns with your desired error handling strategy.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 36-36: Use FQCN for builtin module actions (command).

---

[warning] 36-36: All names should start with an uppercase letter.

</details>

</details>

---

`45-55`: **DNS Record Creation for API**

The DNS record is created using the `amazon.aws.route53` module with parameters that seem aligned with the requirements. Verify that the domain values and TTL meet your infrastructure needs.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 45-45: All names should start with an uppercase letter.

</details>

</details>

</details>
<details>
<summary>roles/infrastructure/ingress/tasks/create_ingress_infra.yaml (3)</summary>

`1-9`: **Wait for Agents Availability**

The task retrieves agent information from the `"hardware-inventory"` namespace with the appropriate label selector. This is a sound approach for ensuring the agents are available before proceeding.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ agent_wait_retries|default(resource_wait_retries) }} -> {{ agent_wait_retries | default(resource_wait_retries) }}

---

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ agent_wait_delay|default(resource_wait_delay) }} -> {{ agent_wait_delay | default(resource_wait_delay) }}

</details>

</details>

---

`37-44`: **Ingress Port Forwarding Setup**

The command task to create port forwarding for ingress is well-constructed. It handles duplicate port forwarding errors gracefully. Double-check that ports 80 and 443 are correct for your ingress configuration and that the error condition is acceptable.

---

`45-54`: **Ingress DNS Record Configuration**

The DNS record creation via `amazon.aws.route53` is set up to create a wildcard record for `*.apps.<hosted_cluster_name>.<base_domain>`. Ensure this matches your desired network routing and that the TTL and overwrite options are as needed.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-09T14:41:31Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 1**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml (1)</summary><blockquote>

`35-41`: _🛠️ Refactor suggestion_

**Ingress Role Variable Naming**

Similarly, update the variables passed to the `infrastructure/ingress` role to use the `ingress_` prefix:
```diff
-    hosted_cluster_name: "{{ cluster_order.metadata.name }}"
-    hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
-    hosted_cluster_settings: "{{ cluster_settings_result }}"
-    hosted_cluster_state: present
+    ingress_hosted_cluster_name: "{{ cluster_order.metadata.name }}"
+    ingress_hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
+    ingress_hosted_cluster_settings: "{{ cluster_settings_result }}"
+    ingress_hosted_cluster_state: present
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[failure] 40-40: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

---

[failure] 39-39: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[failure] 38-38: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[failure] 37-37: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[failure] 35-35: role-name[path]
Avoid using paths when importing roles. (infrastructure/ingress)

</details>
<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 35-35: Avoid using paths when importing roles. (infrastructure/ingress)

---

[warning] 37-37: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 38-38: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 39-39: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 40-40: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

</details>

</details>

</blockquote></details>

</blockquote></details>

<details>
<summary>♻️ Duplicate comments (3)</summary><blockquote>

<details>
<summary>roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml (1)</summary><blockquote>

`1-7`: _🛠️ Refactor suggestion_

**Role Inclusion and Variable Naming Convention**

To adhere to naming conventions and address pipeline warnings, update the role inclusion to use the fully qualified module name and rename the variable appropriately:
```diff
-  include_role:
-    name: infrastructure
-    tasks_from: destroy_floating_ip.yaml
-  vars:
-    floating_ip_name: "{{ hosted_cluster_name }}-ingress"
+  ansible.builtin.include_role:
+    name: infrastructure
+    tasks_from: destroy_floating_ip.yaml
+  vars:
+    ingress_floating_ip_name: "{{ hosted_cluster_name }}-ingress"
```

</blockquote></details>
<details>
<summary>roles/infrastructure/api/tasks/destroy_api_infra.yaml (1)</summary><blockquote>

`12-18`: _🛠️ Refactor suggestion_

**API Floating IP Role Inclusion and Variable Naming**

Update the role inclusion to use the fully qualified module name and adjust the variable naming to use the `api_` prefix:
```diff
-  include_role:
-    name: infrastructure
-    tasks_from: destroy_floating_ip.yaml
-  vars:
-    floating_ip_name: "{{ hosted_cluster_name }}-api"
+  ansible.builtin.include_role:
+    name: infrastructure
+    tasks_from: destroy_floating_ip.yaml
+  vars:
+    api_floating_ip_name: "{{ hosted_cluster_name }}-api"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 12-12: Use FQCN for builtin module actions (include_role).

---

[warning] 17-17: Variables names from within roles should use infrastructure_ as a prefix. (vars: floating_ip_name)

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/ingress/tasks/create_ingress_infra.yaml (1)</summary><blockquote>

`26-32`: **Standardize Task Naming and Variable for Ingress Floating IP Allocation**  
Change the task name to "Allocate Ingress Floating IP" (capitalize the first word) and update the variable name from `floating_ip_name` to `ingress_floating_ip_name` for clarity. Also, use the fully-qualified version of `include_role` (e.g. `ansible.builtin.include_role`).  
```diff
- - name: allocate ingress floating ip
-   include_role:
-     name: infrastructure
-     tasks_from: allocate_floating_ip.yaml
-   vars:
-     floating_ip_name: "{{ hosted_cluster_name }}-ingress"
+ - name: Allocate Ingress Floating IP
+   ansible.builtin.include_role:
+     name: infrastructure
+     tasks_from: allocate_floating_ip.yaml
+   vars:
+     ingress_floating_ip_name: "{{ hosted_cluster_name }}-ingress"
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (20)</summary><blockquote>

<details>
<summary>roles/infrastructure/tasks/destroy_floating_ip.yaml (1)</summary><blockquote>

`9-11`: **Improve Jinja2 Spacing in Set Fact**

Improve readability by adding spaces within the Jinja2 expression:
```diff
-      destroy_floating_ip: "{{fips.floating_ips[0].floating_ip_address}}"
+      destroy_floating_ip: "{{ fips.floating_ips[0].floating_ip_address }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 10-10: jinja[spacing]
Jinja2 spacing could be improved: {{fips.floating_ips[0].floating_ip_address}} -> {{ fips.floating_ips[0].floating_ip_address }}

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml (1)</summary><blockquote>

`8-15`: **DNS Record Task: Jinja2 Spacing Improvement**

Improve the formatting of the Jinja2 expression for the DNS record:
```diff
-    record: "*.apps.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}}"
+    record: "*.apps.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 10-10: jinja[spacing]
Jinja2 spacing could be improved: *.apps.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}} -> *.apps.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/api/tasks/destroy_api_infra.yaml (2)</summary><blockquote>

`1-11`: **Task Name Capitalization**

Consider capitalizing the task name for consistency. For example:
```diff
- name: wait for hostedcluster to finish deleting
+ name: Wait for HostedCluster to Finish Deleting
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 1-1: All names should start with an uppercase letter.

</details>

</details>

---

`19-26`: **API DNS Record Task: Spacing and Naming Consistency**

Ensure proper Jinja2 spacing in the DNS record string and consider capitalizing the task name if needed. For example:
```diff
-    record: "api.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}}"
+    record: "api.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 21-21: jinja[spacing]
Jinja2 spacing could be improved: api.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}} -> api.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}

</details>
<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 19-19: All names should start with an uppercase letter.

---

[warning] 21-21: Jinja2 spacing could be improved: api.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}} -> api.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/tasks/allocate_floating_ip.yaml (2)</summary><blockquote>

`6-10`: **Improve Jinja2 Spacing in Set Fact**

Enhance clarity by improving the spacing within the Jinja2 expression:
```diff
-    allocate_floating_ip_result: "{{fips.floating_ips[0].floating_ip_address}}"
+    allocate_floating_ip_result: "{{ fips.floating_ips[0].floating_ip_address }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 9-9: jinja[spacing]
Jinja2 spacing could be improved: {{fips.floating_ips[0].floating_ip_address}} -> {{ fips.floating_ips[0].floating_ip_address }}

</details>

</details>

---

`19-21`: **Command Output Parsing with Jinja2 Spacing**

Improve the formatting for parsing the command output:
```diff
-      fip_cmd_result: "{{ fip_cmd_raw.stdout|from_json }}"
+      fip_cmd_result: "{{ fip_cmd_raw.stdout | from_json }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 20-20: jinja[spacing]
Jinja2 spacing could be improved: {{ fip_cmd_raw.stdout|from_json }} -> {{ fip_cmd_raw.stdout | from_json }}

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/api/tasks/create_api_infra.yaml (8)</summary><blockquote>

`1-8`: **Task Naming Convention: Capitalize the Task Name**  
The task name on line 1 ("get kube-apiserver service") should start with an uppercase letter (for example, "Get Kube-apiserver Service") to comply with naming conventions and pipeline requirements.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ kube_apiserver_wait_retries|default(resource_wait_retries) }} -> {{ kube_apiserver_wait_retries | default(resource_wait_retries) }}

---

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ kube_apiserver_wait_delay|default(resource_wait_delay) }} -> {{ kube_apiserver_wait_delay | default(resource_wait_delay) }}

</details>
<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 1-1: Jinja2 spacing could be improved: {{ kube_apiserver_wait_delay|default(resource_wait_delay) }} -> {{ kube_apiserver_wait_delay | default(resource_wait_delay) }}

---

[warning] 1-1: Jinja2 spacing could be improved: {{ kube_apiserver_wait_retries|default(resource_wait_retries) }} -> {{ kube_apiserver_wait_retries | default(resource_wait_retries) }}

---

[warning] 1-1: All names should start with an uppercase letter.

</details>

</details>

---

`9-15`: **Improve Jinja Spacing for Wait Parameters**  
On lines 13–14, add spaces around the Jinja2 filter pipe for clarity and to resolve lint warnings. For example:  
```diff
-  retries: "{{ kube_apiserver_wait_retries|default(resource_wait_retries) }}"
-  delay: "{{ kube_apiserver_wait_delay|default(resource_wait_delay) }}"
+  retries: "{{ kube_apiserver_wait_retries | default(resource_wait_retries) }}"
+  delay: "{{ kube_apiserver_wait_delay | default(resource_wait_delay) }}"
```

---

`16-19`: **Standardize Task Name and Module Reference for Setting API Internal IP**  
Rename the task ("set api_internal_ip") to "Set API Internal IP" (capitalizing the first letter) and consider using the fully-qualified module name (e.g. `ansible.builtin.set_fact`) for improved clarity and lint compliance.  
```diff
- - name: set api_internal_ip
-   set_fact:
+ - name: Set API Internal IP
+   ansible.builtin.set_fact:
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 16-16: Use FQCN for builtin module actions (set_fact).

---

[warning] 16-16: All names should start with an uppercase letter.

</details>

</details>

---

`20-26`: **Standardize Task Naming and Variable Naming for Floating IP Allocation**  
Update the task name to "Allocate API Floating IP" (capitalize the first letter) and rename the variable from `floating_ip_name` to a more descriptive name like `api_floating_ip_name` so that it aligns with role-specific conventions. Also, use the fully-qualified module name for the role inclusion (e.g. `ansible.builtin.include_role`) if possible.  
```diff
- - name: allocate api floating ip
-   include_role:
-     name: infrastructure
-     tasks_from: allocate_floating_ip
-   vars:
-     floating_ip_name: "{{ hosted_cluster_name }}-api"
+ - name: Allocate API Floating IP
+   ansible.builtin.include_role:
+     name: infrastructure
+     tasks_from: allocate_floating_ip
+   vars:
+     api_floating_ip_name: "{{ hosted_cluster_name }}-api"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 20-20: Use FQCN for builtin module actions (include_role).

---

[warning] 20-20: All names should start with an uppercase letter.

---

[warning] 25-25: Variables names from within roles should use infrastructure_ as a prefix. (vars: floating_ip_name)

</details>

</details>

---

`27-30`: **Enhance Task Consistency When Setting API Floating IP**  
Rename the task ("set api_floating_ip") to "Set API Floating IP" and use the fully-qualified module name for `set_fact` (e.g. `ansible.builtin.set_fact`).  
```diff
- - name: set api_floating_ip
-   set_fact:
-     api_floating_ip: "{{ allocate_floating_ip_result }}"
+ - name: Set API Floating IP
+   ansible.builtin.set_fact:
+     api_floating_ip: "{{ allocate_floating_ip_result }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 27-27: Use FQCN for builtin module actions (set_fact).

---

[warning] 27-27: All names should start with an uppercase letter.

</details>

</details>

---

`31-35`: **Attention: Review the Idempotency Concern in Port Forwarding**  
The FIXME note from lines 31–35 draws attention to potential idempotency issues with the `openstack esi port forwarding create` command. Make sure there is a strategy in place—either via improved error handling or task pre-checks—to manage duplicate port forwarding errors on repeated runs.

---

`36-44`: **Standardize Command Task and Module Usage in Port Forwarding**  
Change the task name ("create port forwarding") to "Create Port Forwarding" (capitalize the first letter) and consider using the fully-qualified module name for the command (e.g. `ansible.builtin.command`) to align with lint recommendations.  
```diff
- - name: create port forwarding
-   command: >-
-     openstack esi port forwarding create {{ api_internal_ip }} {{ api_floating_ip }}
-       --internal-ip-network "{{ internal_ip_network }}"
-       -p {{ kube_apiserver_port }} -d "{{ hosted_cluster_name }}" -f json
+ - name: Create Port Forwarding
+   ansible.builtin.command: >-
+     openstack esi port forwarding create {{ api_internal_ip }} {{ api_floating_ip }}
+       --internal-ip-network "{{ internal_ip_network }}"
+       -p {{ kube_apiserver_port }} -d "{{ hosted_cluster_name }}" -f json
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 36-36: Use FQCN for builtin module actions (command).

---

[warning] 36-36: All names should start with an uppercase letter.

</details>

</details>

---

`45-55`: **Capitalize Task Name for DNS Record Creation**  
Update the task name ("create dns record") to "Create DNS Record" so that it starts with an uppercase letter, meeting naming conventions.  
```diff
- - name: create dns record
+ - name: Create DNS Record
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 45-45: All names should start with an uppercase letter.

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/ingress/tasks/create_ingress_infra.yaml (6)</summary><blockquote>

`1-9`: **Standardize Task Name for Retrieving Agents**  
Rename the task on line 1 from "wait until agents are available" to "Wait Until Agents Are Available" (capitalizing the first word) to conform with naming conventions.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ agent_wait_retries|default(resource_wait_retries) }} -> {{ agent_wait_retries | default(resource_wait_retries) }}

---

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ agent_wait_delay|default(resource_wait_delay) }} -> {{ agent_wait_delay | default(resource_wait_delay) }}

</details>

</details>

---

`12-13`: **Improve Jinja Spacing for Wait Time Variables**  
On lines 12–13, adjust the spacing in the Jinja2 expressions to enhance readability. For example:  
```diff
- retries: "{{ agent_wait_retries|default(resource_wait_retries) }}"
- delay: "{{ agent_wait_delay|default(resource_wait_delay) }}"
+ retries: "{{ agent_wait_retries | default(resource_wait_retries) }}"
+ delay: "{{ agent_wait_delay | default(resource_wait_delay) }}"
```

---

`15-25`: **Enhance Task Naming and Module Reference for Worker Node IP**  
Rename the task ("set worker_node_ip") to "Set Worker Node IP" (using uppercase) and use the fully-qualified module name for `set_fact` (e.g. `ansible.builtin.set_fact`) to satisfy linting guidelines.  
```diff
- - name: set worker_node_ip
-   set_fact:
+ - name: Set Worker Node IP
+   ansible.builtin.set_fact:
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 17-17: jinja[spacing]
Jinja2 spacing could be improved: {{ agents| json_query('resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]')| shuffle| first| ansible.utils.ipaddr('address') }} -> {{ agents | json_query('resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]') | shuffle | first | ansible.utils.ipaddr('address') }}

</details>

</details>

---

`33-36`: **Enhance Task Consistency When Setting Ingress Floating IP**  
Rename the task ("set ingress_floating_ip") to "Set Ingress Floating IP" (with an uppercase first letter) and use the fully-qualified `set_fact` module (i.e. `ansible.builtin.set_fact`) for improved consistency.  
```diff
- - name: set ingress_floating_ip
-   set_fact:
-     ingress_floating_ip: "{{ allocate_floating_ip_result }}"
+ - name: Set Ingress Floating IP
+   ansible.builtin.set_fact:
+     ingress_floating_ip: "{{ allocate_floating_ip_result }}"
```

---

`37-44`: **Standardize Task Naming and Command Module Reference for Ingress Port Forwarding**  
Update the task name to "Create Port Forwarding for Ingress" and consider using the fully-qualified command module (i.e. `ansible.builtin.command`) for compliance with lint guidelines.  
```diff
- - name: create port forwarding for ingress
-   command: >-
-     openstack esi port forwarding create {{ worker_node_ip }} {{ ingress_floating_ip }}
-       -p 80 -p 443 -d "{{ hosted_cluster_name }}-ingress" -f json
+ - name: Create Port Forwarding for Ingress
+   ansible.builtin.command: >-
+     openstack esi port forwarding create {{ worker_node_ip }} {{ ingress_floating_ip }}
+       -p 80 -p 443 -d "{{ hosted_cluster_name }}-ingress" -f json
```

---

`45-55`: **Capitalize Task Name for Ingress DNS Record Creation**  
Rename the task ("create ingress dns record") to "Create Ingress DNS Record" to ensure that the task name starts with an uppercase letter.  
```diff
- - name: create ingress dns record
+ - name: Create Ingress DNS Record
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 82830aca3acc01a564476372d6d8de076ccf3000 and 13579613a07df64874d6ba77c09464540edcb948.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `uv.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (18)</summary>

* `collections/requirements.yml` (1 hunks)
* `playbook_cloudkit_create_hosted_cluster.yml` (2 hunks)
* `playbook_cloudkit_delete_hosted_cluster.yml` (2 hunks)
* `pyproject.toml` (1 hunks)
* `roles/infrastructure/api/defaults/main.yaml` (1 hunks)
* `roles/infrastructure/api/meta/main.yaml` (1 hunks)
* `roles/infrastructure/api/tasks/create_api_infra.yaml` (1 hunks)
* `roles/infrastructure/api/tasks/destroy_api_infra.yaml` (1 hunks)
* `roles/infrastructure/api/tasks/main.yaml` (1 hunks)
* `roles/infrastructure/common/defaults/main.yaml` (1 hunks)
* `roles/infrastructure/defaults/main.yaml` (1 hunks)
* `roles/infrastructure/ingress/meta/main.yaml` (1 hunks)
* `roles/infrastructure/ingress/tasks/create_ingress_infra.yaml` (1 hunks)
* `roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml` (1 hunks)
* `roles/infrastructure/ingress/tasks/main.yaml` (1 hunks)
* `roles/infrastructure/meta/main.yaml` (1 hunks)
* `roles/infrastructure/tasks/allocate_floating_ip.yaml` (1 hunks)
* `roles/infrastructure/tasks/destroy_floating_ip.yaml` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (11)</summary>

* roles/infrastructure/api/meta/main.yaml
* roles/infrastructure/ingress/tasks/main.yaml
* roles/infrastructure/defaults/main.yaml
* roles/infrastructure/ingress/meta/main.yaml
* roles/infrastructure/api/tasks/main.yaml
* pyproject.toml
* collections/requirements.yml
* roles/infrastructure/api/defaults/main.yaml
* playbook_cloudkit_delete_hosted_cluster.yml
* roles/infrastructure/meta/main.yaml
* roles/infrastructure/common/defaults/main.yaml

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml</summary>

[failure] 40-40: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

---

[failure] 39-39: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[failure] 38-38: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[failure] 37-37: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[failure] 35-35: role-name[path]
Avoid using paths when importing roles. (infrastructure/ingress)

---

[failure] 33-33: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

---

[failure] 32-32: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[failure] 31-31: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[failure] 30-30: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

---

[failure] 28-28: role-name[path]
Avoid using paths when importing roles. (infrastructure/api)

</details>
<details>
<summary>roles/infrastructure/api/tasks/destroy_api_infra.yaml</summary>

[warning] 21-21: jinja[spacing]
Jinja2 spacing could be improved: api.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}} -> api.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}

</details>
<details>
<summary>roles/infrastructure/ingress/tasks/create_ingress_infra.yaml</summary>

[warning] 17-17: jinja[spacing]
Jinja2 spacing could be improved: {{ agents| json_query('resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]')| shuffle| first| ansible.utils.ipaddr('address') }} -> {{ agents | json_query('resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]') | shuffle | first | ansible.utils.ipaddr('address') }}

---

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ agent_wait_retries|default(resource_wait_retries) }} -> {{ agent_wait_retries | default(resource_wait_retries) }}

---

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ agent_wait_delay|default(resource_wait_delay) }} -> {{ agent_wait_delay | default(resource_wait_delay) }}

</details>
<details>
<summary>roles/infrastructure/api/tasks/create_api_infra.yaml</summary>

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ kube_apiserver_wait_retries|default(resource_wait_retries) }} -> {{ kube_apiserver_wait_retries | default(resource_wait_retries) }}

---

[warning] 1-1: jinja[spacing]
Jinja2 spacing could be improved: {{ kube_apiserver_wait_delay|default(resource_wait_delay) }} -> {{ kube_apiserver_wait_delay | default(resource_wait_delay) }}

</details>
<details>
<summary>roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml</summary>

[warning] 10-10: jinja[spacing]
Jinja2 spacing could be improved: *.apps.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}} -> *.apps.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}

</details>
<details>
<summary>roles/infrastructure/tasks/allocate_floating_ip.yaml</summary>

[warning] 20-20: jinja[spacing]
Jinja2 spacing could be improved: {{ fip_cmd_raw.stdout|from_json }} -> {{ fip_cmd_raw.stdout | from_json }}

---

[warning] 9-9: jinja[spacing]
Jinja2 spacing could be improved: {{fips.floating_ips[0].floating_ip_address}} -> {{ fips.floating_ips[0].floating_ip_address }}

</details>
<details>
<summary>roles/infrastructure/tasks/destroy_floating_ip.yaml</summary>

[warning] 10-10: jinja[spacing]
Jinja2 spacing could be improved: {{fips.floating_ips[0].floating_ip_address}} -> {{ fips.floating_ips[0].floating_ip_address }}

</details>

</details>
<details>
<summary>🪛 GitHub Actions: CI</summary>

<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml</summary>

[warning] 28-28: Avoid using paths when importing roles. (infrastructure/api)

---

[warning] 30-30: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 31-31: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 32-32: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 33-33: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

---

[warning] 35-35: Avoid using paths when importing roles. (infrastructure/ingress)

---

[warning] 37-37: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 38-38: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 39-39: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 40-40: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

</details>
<details>
<summary>roles/infrastructure/api/tasks/destroy_api_infra.yaml</summary>

[warning] 1-1: All names should start with an uppercase letter.

---

[warning] 12-12: Use FQCN for builtin module actions (include_role).

---

[warning] 17-17: Variables names from within roles should use infrastructure_ as a prefix. (vars: floating_ip_name)

---

[warning] 19-19: All names should start with an uppercase letter.

---

[warning] 21-21: Jinja2 spacing could be improved: api.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}} -> api.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}

</details>
<details>
<summary>roles/infrastructure/api/tasks/create_api_infra.yaml</summary>

[warning] 1-1: Jinja2 spacing could be improved: {{ kube_apiserver_wait_delay|default(resource_wait_delay) }} -> {{ kube_apiserver_wait_delay | default(resource_wait_delay) }}

---

[warning] 1-1: Jinja2 spacing could be improved: {{ kube_apiserver_wait_retries|default(resource_wait_retries) }} -> {{ kube_apiserver_wait_retries | default(resource_wait_retries) }}

---

[warning] 1-1: All names should start with an uppercase letter.

---

[warning] 16-16: Use FQCN for builtin module actions (set_fact).

---

[warning] 16-16: All names should start with an uppercase letter.

---

[warning] 20-20: Use FQCN for builtin module actions (include_role).

---

[warning] 20-20: All names should start with an uppercase letter.

---

[warning] 25-25: Variables names from within roles should use infrastructure_ as a prefix. (vars: floating_ip_name)

---

[warning] 27-27: Use FQCN for builtin module actions (set_fact).

---

[warning] 27-27: All names should start with an uppercase letter.

---

[warning] 36-36: Use FQCN for builtin module actions (command).

---

[warning] 36-36: All names should start with an uppercase letter.

---

[warning] 45-45: All names should start with an uppercase letter.

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (10)</summary><blockquote>

<details>
<summary>roles/infrastructure/tasks/destroy_floating_ip.yaml (5)</summary>

`1-4`: **Floating IP Lookup Task Validation**

Ensure that the `openstack.cloud.floating_ip_info` module is properly fetching the floating IP using the given description. Confirm that the variable `floating_ip_name` is correctly defined and matches the expected naming convention.

---

`6-8`: **Conditional Execution Block**

The use of the `when: fips.floating_ips` condition attached to the block is appropriate to ensure the following tasks run only when a floating IP is found. Verify this condition accurately handles all cases (e.g., empty lists).

---

`12-15`: **Port Forwarding Purge Command Check**

Confirm that the command:
```
openstack esi port forwarding purge {{ destroy_floating_ip }} -f json
```
is valid and reliably purges the associated port forwardings. Consider adding error handling or comments to document expected outcomes.

---

`16-19`: **Unset Floating IP Port Command Review**

Verify that the command to unset the floating IP port is correctly structured and that the variable substitution for `destroy_floating_ip` meets OpenStack CLI expectations.

---

`20-23`: **Delete Floating IP Command Validation**

Ensure that the command for deleting the floating IP:
```
openstack floating ip delete {{ destroy_floating_ip }}
```
correctly deletes the floating IP and that any potential errors are either handled or documented.

</details>
<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml (2)</summary>

`5-7`: **Environment Variable Configuration**

The environment variable `OS_CLOUD` is set with a default value using the filter. Verify that `openstack_cloud_name` provides the correct input when necessary.

---

`19-20`: **Template Parameters JSON Parsing**

Parsing `cluster_order.spec.templateParameters` with the `from_json` filter is a useful enhancement. Ensure that the resulting JSON structure is as expected.

</details>
<details>
<summary>roles/infrastructure/tasks/allocate_floating_ip.yaml (3)</summary>

`1-4`: **Floating IP Existence Check**

Ensure the lookup with `openstack.cloud.floating_ip_info` is correctly configured with the provided `floating_ip_name`. Verify that the description accurately matches the expected floating IP.

---

`11-18`: **Floating IP Allocation Block Review**

Verify that the block for allocating a new floating IP (when none exists) is structured correctly. Confirm that `floating_ip_network` and `floating_ip_name` are set properly and that the command output (in JSON format) is correctly registered.
```diff
-      openstack floating ip create {{ floating_ip_network }} --description "{{ floating_ip_name }}"
-        -f json
+      openstack floating ip create {{ floating_ip_network }} --description "{{ floating_ip_name }}" -f json
```

---

`22-25`: **Set Fact for New Floating IP**

The task correctly assigns the newly allocated IP to `allocate_floating_ip_result`. Confirm that the JSON output contains a valid `floating_ip_address` key.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-09T14:58:19Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>♻️ Duplicate comments (7)</summary><blockquote>

<details>
<summary>roles/infrastructure/api/defaults/main.yaml (1)</summary><blockquote>

`1-2`: **Variable Naming Convention:**  
The configuration parameters `api_kube_apiserver_port` and `api_internal_ip_network` are introduced here. To align with our team’s naming standards—which typically require an `infrastructure_` prefix—it is recommended to rename these variables (e.g. to `infrastructure_api_kube_apiserver_port` and `infrastructure_api_internal_ip_network`) if that fits your overall convention.

</blockquote></details>
<details>
<summary>roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml (1)</summary><blockquote>

`5-6`: **Variable Naming Consistency:**  
Consider renaming the variable `floating_ip_name` to `infrastructure_floating_ip_name` so that it remains consistent with our naming guidelines across similar roles.

</blockquote></details>
<details>
<summary>roles/infrastructure/api/tasks/destroy_api_infra.yaml (1)</summary><blockquote>

`12-18`: **Variable Naming for Floating IP:**  
The variable used here is `floating_ip_name`. For consistency with our naming conventions—as noted in previous reviews—please consider renaming it to `infrastructure_floating_ip_name`. For example:  
```diff
-    floating_ip_name: "{{ hosted_cluster_name }}-api"
+    infrastructure_floating_ip_name: "{{ hosted_cluster_name }}-api"
```

</blockquote></details>
<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml (2)</summary><blockquote>

`28-33`: _🛠️ Refactor suggestion_

**API Role Variable Naming & Role Import Path**  
The tasks for the `infrastructure/api` role still pass variables with generic names (e.g. `hosted_cluster_name`) and use a role path. To comply with naming conventions and avoid path-based role imports, consider renaming variables to use an `api_` prefix (e.g. `api_hosted_cluster_name`, `api_hosted_cluster_namespace`, etc.) and update the role reference accordingly.  
For example:  
```diff
-    hosted_cluster_name: "{{ cluster_order.metadata.name }}"
-    hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
-    hosted_cluster_settings: "{{ cluster_settings_result }}"
-    hosted_cluster_state: present
+    api_hosted_cluster_name: "{{ cluster_order.metadata.name }}"
+    api_hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
+    api_hosted_cluster_settings: "{{ cluster_settings_result }}"
+    api_hosted_cluster_state: present
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[failure] 33-33: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

---

[failure] 32-32: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[failure] 31-31: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[failure] 30-30: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

---

[failure] 28-28: role-name[path]
Avoid using paths when importing roles. (infrastructure/api)

</details>

</details>

---

`35-40`: _🛠️ Refactor suggestion_

**Ingress Role Variable Naming & Role Import Path**  
Similarly, the `infrastructure/ingress` role receives generic variable names. To better reflect its purpose and adhere to naming conventions, rename these variables to use an `ingress_` prefix (e.g. `ingress_hosted_cluster_name`, etc.), and avoid using path-based role imports.  
For example:  
```diff
-    hosted_cluster_name: "{{ cluster_order.metadata.name }}"
-    hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
-    hosted_cluster_settings: "{{ cluster_settings_result }}"
-    hosted_cluster_state: present
+    ingress_hosted_cluster_name: "{{ cluster_order.metadata.name }}"
+    ingress_hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
+    ingress_hosted_cluster_settings: "{{ cluster_settings_result }}"
+    ingress_hosted_cluster_state: present
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[failure] 40-40: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

---

[failure] 39-39: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[failure] 38-38: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[failure] 37-37: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[failure] 35-35: role-name[path]
Avoid using paths when importing roles. (infrastructure/ingress)

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/api/tasks/create_api_infra.yaml (1)</summary><blockquote>

`20-25`: _🛠️ Refactor suggestion_

**API Floating IP Variable Naming**  
In the task that includes the floating IP allocation role, the variable is set as:  
```yaml
floating_ip_name: "{{ hosted_cluster_name }}-api"
```  
For clarity and consistency with API-related tasks, please consider renaming this to `api_floating_ip_name`.  
For example:  
```diff
-    floating_ip_name: "{{ hosted_cluster_name }}-api"
+    api_floating_ip_name: "{{ hosted_cluster_name }}-api"
```

</blockquote></details>
<details>
<summary>roles/infrastructure/ingress/tasks/create_ingress_infra.yaml (1)</summary><blockquote>

`30-32`: _🛠️ Refactor suggestion_

**Ingress Floating IP Variable Naming**  
The variable for the floating IP is defined as:  
```yaml
floating_ip_name: "{{ hosted_cluster_name }}-ingress"
```  
To align with ingress-specific naming conventions, consider renaming it to `ingress_floating_ip_name`.  
```diff
-    floating_ip_name: "{{ hosted_cluster_name }}-ingress"
+    ingress_floating_ip_name: "{{ hosted_cluster_name }}-ingress"
```

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (6)</summary><blockquote>

<details>
<summary>roles/infrastructure/tasks/destroy_floating_ip.yaml (2)</summary><blockquote>

`6-7`: **Task Name Clarity:**  
Consider renaming the task **"Destroy floating floating_ip_address"** to a clearer title such as **"Destroy floating IP"** to avoid redundancy and improve readability.

---

`10-11`: **Jinja Formatting Improvement:**  
Improve the spacing in the Jinja template for consistency with our style guidelines. For example, update:  
```diff
-      destroy_floating_ip: "{{fips.floating_ips[0].floating_ip_address}}"
+      destroy_floating_ip: "{{ fips.floating_ips[0].floating_ip_address }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 11-11: jinja[spacing]
Jinja2 spacing could be improved: {{fips.floating_ips[0].floating_ip_address}} -> {{ fips.floating_ips[0].floating_ip_address }}

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml (1)</summary><blockquote>

`12-12`: **Jinja Spacing in DNS Record:**  
The DNS record template would benefit from improved spacing. For example, change:  
```diff
-    record: "*.apps.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}}"
+    record: "*.apps.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}"
```  
This helps ensure consistent readability and formatting.

</blockquote></details>
<details>
<summary>roles/infrastructure/tasks/allocate_floating_ip.yaml (2)</summary><blockquote>

`8-9`: **Jinja Expression Spacing**  
Improve the readability of the Jinja expression by adding spaces inside the curly braces. For instance, update:  
```yaml
"{{fips.floating_ips[0].floating_ip_address}}"
```  
to  
```yaml
"{{ fips.floating_ips[0].floating_ip_address }}"
```

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 9-9: jinja[spacing]
Jinja2 spacing could be improved: {{fips.floating_ips[0].floating_ip_address}} -> {{ fips.floating_ips[0].floating_ip_address }}

</details>

</details>

---

`13-17`: **Consider Using a Dedicated Module for Floating IP Creation**  
Instead of using the `ansible.builtin.command` to run `openstack floating ip create`, check if an Ansible module (such as those provided by the OpenStack collection) is available. This would improve idempotence and error handling.

</blockquote></details>
<details>
<summary>roles/infrastructure/ingress/tasks/create_ingress_infra.yaml (1)</summary><blockquote>

`26-26`: **Role Import Path Warning for Ingress**  
The inclusion of the floating IP allocation role using a path (via `name: Infrastructure`) may benefit from a refactoring to reference the role using a normalized name rather than a path.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 13579613a07df64874d6ba77c09464540edcb948 and d76ed28f360d0b996aed2aa91dda03eed32e1111.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `uv.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (18)</summary>

* `collections/requirements.yml` (1 hunks)
* `playbook_cloudkit_create_hosted_cluster.yml` (2 hunks)
* `playbook_cloudkit_delete_hosted_cluster.yml` (2 hunks)
* `pyproject.toml` (1 hunks)
* `roles/infrastructure/api/defaults/main.yaml` (1 hunks)
* `roles/infrastructure/api/meta/main.yaml` (1 hunks)
* `roles/infrastructure/api/tasks/create_api_infra.yaml` (1 hunks)
* `roles/infrastructure/api/tasks/destroy_api_infra.yaml` (1 hunks)
* `roles/infrastructure/api/tasks/main.yaml` (1 hunks)
* `roles/infrastructure/common/defaults/main.yaml` (1 hunks)
* `roles/infrastructure/defaults/main.yaml` (1 hunks)
* `roles/infrastructure/ingress/meta/main.yaml` (1 hunks)
* `roles/infrastructure/ingress/tasks/create_ingress_infra.yaml` (1 hunks)
* `roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml` (1 hunks)
* `roles/infrastructure/ingress/tasks/main.yaml` (1 hunks)
* `roles/infrastructure/meta/main.yaml` (1 hunks)
* `roles/infrastructure/tasks/allocate_floating_ip.yaml` (1 hunks)
* `roles/infrastructure/tasks/destroy_floating_ip.yaml` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (9)</summary>

* roles/infrastructure/defaults/main.yaml
* roles/infrastructure/ingress/meta/main.yaml
* roles/infrastructure/meta/main.yaml
* roles/infrastructure/api/tasks/main.yaml
* roles/infrastructure/api/meta/main.yaml
* collections/requirements.yml
* playbook_cloudkit_delete_hosted_cluster.yml
* pyproject.toml
* roles/infrastructure/ingress/tasks/main.yaml

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

<details>
<summary>roles/infrastructure/tasks/allocate_floating_ip.yaml</summary>

[warning] 9-9: jinja[spacing]
Jinja2 spacing could be improved: {{fips.floating_ips[0].floating_ip_address}} -> {{ fips.floating_ips[0].floating_ip_address }}

</details>
<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml</summary>

[failure] 40-40: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

---

[failure] 39-39: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[failure] 38-38: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[failure] 37-37: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[failure] 35-35: role-name[path]
Avoid using paths when importing roles. (infrastructure/ingress)

---

[failure] 33-33: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

---

[failure] 32-32: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[failure] 31-31: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[failure] 30-30: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

---

[failure] 28-28: role-name[path]
Avoid using paths when importing roles. (infrastructure/api)

</details>
<details>
<summary>roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml</summary>

[warning] 10-10: jinja[spacing]
Jinja2 spacing could be improved: *.apps.{{ hosted_cluster_name }}.{{hosted_cluster_settings.base_domain}} -> *.apps.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}

</details>
<details>
<summary>roles/infrastructure/tasks/destroy_floating_ip.yaml</summary>

[warning] 11-11: jinja[spacing]
Jinja2 spacing could be improved: {{fips.floating_ips[0].floating_ip_address}} -> {{ fips.floating_ips[0].floating_ip_address }}

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (16)</summary><blockquote>

<details>
<summary>roles/infrastructure/common/defaults/main.yaml (1)</summary>

`1-3`: **LGTM!**  
The new parameters (`infrastructure_dns_ttl`, `resource_wait_retries`, and `resource_wait_delay`) are clearly defined and follow our naming conventions.

</details>
<details>
<summary>roles/infrastructure/api/tasks/destroy_api_infra.yaml (2)</summary>

`1-10`: **HostedCluster Deletion Wait:**  
The task waiting for the HostedCluster to finish deleting is well configured. Just ensure that the condition `until: not hosted_cluster_check.resources` reliably detects deletion under all expected scenarios.

---

`19-26`: **API DNS Record Deletion:**  
The task handling deletion of the API DNS record is clearly laid out and correctly configured.

</details>
<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml (2)</summary>

`5-6`: **OS_CLOUD Environment Variable Added**  
The addition of the `OS_CLOUD` variable (defaulting to `'devstack'`) in the environment block is clear and appropriately ensures the cloud context is set.

---

`17-20`: **Improved JSON Parameter Parsing**  
Updating `cluster_settings_template_parameters` to use the `| from_json` filter enhances robustness in handling JSON data from `cluster_order.spec.templateParameters`.

</details>
<details>
<summary>roles/infrastructure/tasks/allocate_floating_ip.yaml (1)</summary>

`19-24`: **Parsing Command Output Caution**  
The use of `from_json` to parse the command’s output is a good practice. However, please verify that the JSON structure always includes the key `floating_ip_address` so that `allocate_floating_ip_result` is reliably set.

</details>
<details>
<summary>roles/infrastructure/api/tasks/create_api_infra.yaml (5)</summary>

`1-8`: **Kube-apiserver Service Retrieval**  
The task that retrieves the `kube-apiserver` service using `kubernetes.core.k8s_info` is well implemented. Ensure that any potential absence of the service is handled appropriately downstream.

---

`9-15`: **Wait Mechanism for Load Balancer IP**  
The retry mechanism waiting for the service to have a load balancer IP is a sound approach. Confirm that the retry and delay values (via `kube_apiserver_wait_retries` and `kube_apiserver_wait_delay`) are tuned for your environment.

---

`16-19`: **Setting API Internal IP**  
The `set_fact` task correctly extracts the API internal IP from the service’s load balancer ingress. It may be beneficial to add a safeguard to ensure that the list indexes (e.g. `[0]`) are valid in all scenarios.

---

`27-30`: **Setting API Floating IP Fact**  
The task that assigns `api_floating_ip` from `allocate_floating_ip_result` is straightforward. Verify that the allocation role consistently returns a valid floating IP address.

---

`45-55`: **DNS Record Creation Task**  
The DNS record creation via `amazon.aws.route53` appears well‑structured. Ensure that the TTL and wait parameters align with your desired DNS propagation behavior.

</details>
<details>
<summary>roles/infrastructure/ingress/tasks/create_ingress_infra.yaml (5)</summary>

`1-8`: **Agent Retrieval for Ingress Setup**  
The task fetching Agent resources from the `hardware-inventory` namespace is implemented correctly. Make sure the label selector reliably matches the intended agents for the cluster.

---

`11-14`: **Wait Mechanism for Agent Availability**  
The retry logic waiting until agents are available is appropriate. Verify that the variables `agent_wait_retries` and `agent_wait_delay` are set optimally for your environment’s timing characteristics.

---

`15-25`: **Determining Worker Node IP**  
The `set_fact` task for extracting the worker node IP using a JSON query, shuffle, and selection is clever. Please ensure that the query always returns a valid IP and consider adding error handling if no IPs are found.

---

`33-36`: **Setting Ingress Floating IP**  
The task to set `ingress_floating_ip` from the allocation result is clear. Confirm that the included role reliably sets `allocate_floating_ip_result` in all cases.

---

`45-55`: **Ingress DNS Record Creation**  
The DNS record creation for ingress is implemented correctly with appropriate parameters. Double‑check that the wildcard record `*.apps.{{ hosted_cluster_name }}.{{ hosted_cluster_settings.base_domain }}` aligns with your DNS design.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-09T15:08:06Z
user: login: adriengentil
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T16:00:16Z
user: login: larsks
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T02:04:44Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>♻️ Duplicate comments (7)</summary><blockquote>

<details>
<summary>playbook_cloudkit_delete_hosted_cluster.yml (2)</summary><blockquote>

`28-34`: _🛠️ Refactor suggestion_

**API Role Variable Naming Convention**  
The variables passed to the `infrastructure/api` role (lines 28–34) do not use the recommended `api_` prefix. For consistency and to address pipeline naming warnings, consider renaming these variables (e.g. use `api_hosted_cluster_name`, `api_hosted_cluster_namespace`, etc.).

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 31-31: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 30-30: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

</details>

</details>

---

`35-40`: _🛠️ Refactor suggestion_

**Ingress Role Variable Naming Convention**  
The variables for the `infrastructure/ingress` role should be prefixed with `ingress_` (e.g. `ingress_hosted_cluster_name`) to comply with the naming conventions indicated by previous reviews and pipeline warnings.

</blockquote></details>
<details>
<summary>roles/infrastructure/api/tasks/destroy_api_infra.yaml (1)</summary><blockquote>

`12-18`: _🛠️ Refactor suggestion_

**API Floating IP Variable Naming**  
Within the "Destroy api floating ip" task, the variable `floating_ip_name` is used. Per the naming convention recommendations, it should be renamed to something like `infrastructure_floating_ip_name` to clearly identify its scope and origin.

</blockquote></details>
<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml (2)</summary><blockquote>

`28-34`: _🛠️ Refactor suggestion_

**Enforce API Role Variable Naming Convention**  
For the `infrastructure/api` role, the variables (lines 28–34) should use an `api_` prefix (e.g. `api_hosted_cluster_name`) to align with naming conventions and past review comments.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 33-33: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

---

[warning] 32-32: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 31-31: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 30-30: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

</details>

</details>

---

`35-40`: _🛠️ Refactor suggestion_

**Enforce Ingress Role Variable Naming Convention**  
Similarly, for the `infrastructure/ingress` role, updating the variable prefixes to `ingress_` (e.g. `ingress_hosted_cluster_name`) will improve consistency and clarity.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[warning] 40-40: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

---

[warning] 39-39: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 38-38: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 37-37: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

</details>

</details>

</blockquote></details>
<details>
<summary>roles/infrastructure/api/tasks/create_api_infra.yaml (1)</summary><blockquote>

`20-26`: **Allocate API Floating IP – Variable Naming**
The role inclusion for allocating a floating IP is implemented correctly. However, the variable `floating_ip_name` could be renamed to `api_floating_ip_name` (or prefixed appropriately such as `infrastructure_api_floating_ip_name`) for clarity and consistency with naming conventions within infrastructure roles. This suggestion echoes previous feedback.

</blockquote></details>
<details>
<summary>roles/infrastructure/ingress/tasks/create_ingress_infra.yaml (1)</summary><blockquote>

`26-32`: **Allocate Ingress Floating IP – Variable Naming**
The floating IP allocation uses a variable named `floating_ip_name` with the value `"{{ hosted_cluster_name }}-ingress"`. For improved clarity and consistency with a role-specific naming convention, consider renaming this variable to `ingress_floating_ip_name`. Note that earlier reviews indicated this adjustment; please verify that commit 1357961 has been fully propagated.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (5)</summary><blockquote>

<details>
<summary>roles/infrastructure/tasks/allocate_floating_ip.yaml (1)</summary><blockquote>

`11-18`: **New Floating IP Allocation Block & Variable Naming**  
The block to allocate a new floating IP is implemented effectively using the command module. Consider updating the variable `floating_ip_name` to `infrastructure_floating_ip_name` for consistency with similar roles and naming recommendations.

</blockquote></details>
<details>
<summary>roles/infrastructure/api/tasks/create_api_infra.yaml (2)</summary><blockquote>

`9-15`: **Wait Mechanism for Service Availability**
The “until” loop waits until the service exists and provides a load balancer IP. This wait mechanism is appropriate. If possible, consider enhancing the error message or logging when retries are exhausted for easier debugging.

---

`36-44`: **Port Forwarding Command Idempotency**
The port forwarding task uses an `ansible.builtin.command` to invoke the OpenStack ESI client. As noted by the inline FIXME, this command is not idempotent and may fail in cases of duplicate forwarding. Consider adding a pre-check (e.g. query existing port forwarding rules) or exploring an idempotent module/wrapper if one exists. Even documenting the workaround may help future maintainers.

</blockquote></details>
<details>
<summary>roles/infrastructure/ingress/tasks/create_ingress_infra.yaml (2)</summary><blockquote>

`15-25`: **Set Worker Node IP – Expression Complexity**
The fact-setting for `worker_node_ip` uses a combination of `json_query`, `shuffle`, and the `first` filter. While functionally correct, the expression is complex. Consider splitting the logic into intermediate steps or adding inline comments to clarify its intent for future maintainers.

---

`37-44`: **Ingress Port Forwarding – Idempotency Concerns**
Similar to the API port forwarding task, the ingress port forwarding command may encounter non-idempotency issues, as highlighted by the inline FIXME. Consider adding a check for existing port forwarding rules or refactoring to use an idempotent method if available.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between d76ed28f360d0b996aed2aa91dda03eed32e1111 and 8d888bac665eb9829b1b182feef9175ccc39502f.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `uv.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (20)</summary>

* `.ansible-lint-ignore` (1 hunks)
* `.ansible-lint.yml` (1 hunks)
* `collections/requirements.yml` (1 hunks)
* `playbook_cloudkit_create_hosted_cluster.yml` (2 hunks)
* `playbook_cloudkit_delete_hosted_cluster.yml` (2 hunks)
* `pyproject.toml` (1 hunks)
* `roles/infrastructure/api/defaults/main.yaml` (1 hunks)
* `roles/infrastructure/api/meta/main.yaml` (1 hunks)
* `roles/infrastructure/api/tasks/create_api_infra.yaml` (1 hunks)
* `roles/infrastructure/api/tasks/destroy_api_infra.yaml` (1 hunks)
* `roles/infrastructure/api/tasks/main.yaml` (1 hunks)
* `roles/infrastructure/common/defaults/main.yaml` (1 hunks)
* `roles/infrastructure/defaults/main.yaml` (1 hunks)
* `roles/infrastructure/ingress/meta/main.yaml` (1 hunks)
* `roles/infrastructure/ingress/tasks/create_ingress_infra.yaml` (1 hunks)
* `roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml` (1 hunks)
* `roles/infrastructure/ingress/tasks/main.yaml` (1 hunks)
* `roles/infrastructure/meta/main.yaml` (1 hunks)
* `roles/infrastructure/tasks/allocate_floating_ip.yaml` (1 hunks)
* `roles/infrastructure/tasks/destroy_floating_ip.yaml` (1 hunks)

</details>

<details>
<summary>✅ Files skipped from review due to trivial changes (2)</summary>

* .ansible-lint.yml
* .ansible-lint-ignore

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (11)</summary>

* roles/infrastructure/defaults/main.yaml
* roles/infrastructure/meta/main.yaml
* roles/infrastructure/ingress/tasks/main.yaml
* roles/infrastructure/api/meta/main.yaml
* roles/infrastructure/ingress/meta/main.yaml
* roles/infrastructure/api/tasks/main.yaml
* roles/infrastructure/api/defaults/main.yaml
* roles/infrastructure/common/defaults/main.yaml
* pyproject.toml
* roles/infrastructure/ingress/tasks/destroy_ingress_infra.yaml
* collections/requirements.yml

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml</summary>

[warning] 40-40: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

---

[warning] 39-39: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 38-38: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 37-37: var-naming[no-role-prefix]
Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 33-33: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

---

[warning] 32-32: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 31-31: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 30-30: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

</details>
<details>
<summary>playbook_cloudkit_delete_hosted_cluster.yml</summary>

[warning] 31-31: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 30-30: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

</details>

</details>

</details>

<details>
<summary>🔇 Additional comments (19)</summary><blockquote>

<details>
<summary>playbook_cloudkit_delete_hosted_cluster.yml (1)</summary>

`5-6`: **OS_CLOUD Environment Configuration Looks Good**  
The environment block correctly sets the `OS_CLOUD` variable with a sensible default using the Jinja2 filter.

</details>
<details>
<summary>roles/infrastructure/tasks/destroy_floating_ip.yaml (3)</summary>

`1-4`: **Floating IP Lookup Task Validation**  
The task that uses `openstack.cloud.floating_ip_info` to look up the floating IP by description is correctly implemented.

---

`6-12`: **Extracting Floating IP Address for Destruction**  
The extraction of the floating IP address from the first element of `fips.floating_ips` is acceptable; ensure that multiple results are not a concern or are handled elsewhere if needed.

---

`13-24`: **Command Tasks for Floating IP Destruction**  
The block executing commands to purge port forwarding, unset the IP port, and delete the floating IP is clearly structured and uses the appropriate modules. The use of `# noqa:no-changed-when` is appropriate to bypass unnecessary changed status checks.

</details>
<details>
<summary>roles/infrastructure/api/tasks/destroy_api_infra.yaml (2)</summary>

`1-11`: **HostedCluster Deletion Wait Block**  
The waiting loop using `kubernetes.core.k8s_info` to detect deletion of the `HostedCluster` is implemented correctly with a reasonable retry and delay strategy. Consider additional error handling if the resource remains longer than expected.

---

`19-26`: **API DNS Record Deletion Block**  
The task using the `amazon.aws.route53` module to delete the API DNS record is correctly configured with proper parameters.

</details>
<details>
<summary>playbook_cloudkit_create_hosted_cluster.yml (2)</summary>

`5-6`: **OS_CLOUD Environment Variable Set Correctly**  
The playbook correctly sets the `OS_CLOUD` environment variable with a default value, enhancing the cloud configuration.

---

`19-19`: **Template Parameters Parsing Enhancement**  
The use of the `from_json` filter on `cluster_order.spec.templateParameters` (line 19) improves robustness by correctly parsing the JSON string.

</details>
<details>
<summary>roles/infrastructure/tasks/allocate_floating_ip.yaml (3)</summary>

`1-4`: **Floating IP Existence Check Implementation**  
The task checking for the existence of a floating IP using `openstack.cloud.floating_ip_info` is implemented properly.

---

`6-10`: **Extract Existing Floating IP Address**  
The task correctly sets `allocate_floating_ip_result` when a floating IP is found. This is clear and concise.

---

`20-27`: **JSON Unmarshalling for Floating IP Allocation**  
The use of the `from_json` filter on the command output, and the subsequent setting of the new IP fact, is correctly handled.

</details>
<details>
<summary>roles/infrastructure/api/tasks/create_api_infra.yaml (4)</summary>

`1-8`: **Service Retrieval Task**
The task correctly uses `kubernetes.core.k8s_info` to retrieve the kube-apiserver service details and registers the result. This is a standard approach and appears well implemented.

---

`16-19`: **Set API Internal IP**
The task extracts the API internal IP from the service’s load balancer ingress details. This is clear and direct. Be sure that the service always returns at least one ingress entry; otherwise, additional checks might be needed.

---

`27-30`: **Set API Floating IP**
Setting the fact `api_floating_ip` from the output of the floating IP allocation role is straightforward and clear. Ensure that the allocated role reliably populates `allocate_floating_ip_result`.

---

`45-55`: **DNS Record Creation**
The DNS record creation task using `amazon.aws.route53` is implemented properly with the necessary options (`wait`, `overwrite`, etc.). This task is clear and meets its objectives.

</details>
<details>
<summary>roles/infrastructure/ingress/tasks/create_ingress_infra.yaml (4)</summary>

`1-9`: **Wait for Agents Availability**
The task queries for Agent resources in the "hardware-inventory" namespace using `kubernetes.core.k8s_info` with an appropriate label selector based on `hosted_cluster_name`. This setup appears functional.

---

`10-14`: **Wait Until Agents Are Assigned**
The use of a retry loop via the “until” clause (waiting on `agents.resources`) is a good approach to ensure that agents have been assigned before progressing. Confirm that the condition reliably reflects agent assignment.

---

`33-36`: **Set Ingress Floating IP**
This task sets the `ingress_floating_ip` fact based on the output of the floating IP allocation role. The implementation is clear; just ensure that the included role reliably returns `allocate_floating_ip_result`.

---

`45-55`: **Ingress DNS Record Creation**
The creation of the ingress DNS record with `amazon.aws.route53` is implemented correctly, with settings such as wait and overwrite ensuring proper final state management. The usage of wildcard records is appropriate for ingress scenarios.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-11T02:10:15Z
user: login: adriengentil
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T08:59:03Z
user: login: larsks
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T20:27:01Z
user: login: adriengentil
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T08:10:37Z
user: login: adriengentil
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T13:57:25Z

Review comments:
diff_hunk: @@ -0,0 +1,2 @@
+kube_apiserver_port: 6443
+internal_ip_network: hypershift
path: roles/infrastructure/api/defaults/main.yaml
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Variable Naming Convention**:  
The variables `kube_apiserver_port` and `internal_ip_network` do not follow the team’s naming conventions. Please consider renaming them to `infrastructure_kube_apiserver_port` and `infrastructure_internal_ip_network` respectively to align with the guidelines.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 1-1: Variables names from within roles should use infrastructure_ as a prefix. (vars: kube_apiserver_port)

---

[warning] 2-2: Variables names from within roles should use infrastructure_ as a prefix. (vars: internal_ip_network)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T14:41:29Z
author_association: NONE
diff_hunk: @@ -22,3 +24,17 @@
         hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
         hosted_cluster_settings: "{{ cluster_settings_result }}"
         hosted_cluster_state: absent
+
+    - role: infrastructure/api
+      vars:
+        hosted_cluster_name: "{{ cluster_order.metadata.name }}"
+        hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
+        hosted_cluster_settings: "{{ cluster_settings_result }}"
+        hosted_cluster_state: absent
+
path: playbook_cloudkit_delete_hosted_cluster.yml
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Enforce Variable Naming Convention for the API Role.**  
The new `infrastructure/api` role is being imported with variables named `hosted_cluster_name`, `hosted_cluster_namespace`, `hosted_cluster_settings`, and `hosted_cluster_state`. Pipeline warnings recommend using an `api_` prefix (e.g. `api_hosted_cluster_name`) as well as avoiding path-like role names. Please update these variables to match the naming convention and consider referencing the role in a way that avoids using a path.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 28-28: Avoid using paths when importing roles. (infrastructure/api)

---

[warning] 30-30: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 31-31: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 32-32: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 33-33: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T14:41:29Z
author_association: NONE
diff_hunk: @@ -22,3 +24,17 @@
         hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
         hosted_cluster_settings: "{{ cluster_settings_result }}"
         hosted_cluster_state: absent
+
+    - role: infrastructure/api
+      vars:
+        hosted_cluster_name: "{{ cluster_order.metadata.name }}"
+        hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
+        hosted_cluster_settings: "{{ cluster_settings_result }}"
+        hosted_cluster_state: absent
+
+    - role: infrastructure/ingress
+      vars:
+        hosted_cluster_name: "{{ cluster_order.metadata.name }}"
+        hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
+        hosted_cluster_settings: "{{ cluster_settings_result }}"
+        hosted_cluster_state: absent
path: playbook_cloudkit_delete_hosted_cluster.yml
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Enforce Variable Naming Convention for the Ingress Role.**  
The new `infrastructure/ingress` role is added with variables that currently do not follow the recommended naming scheme. Variables should be prefixed with `ingress_` (e.g. `ingress_hosted_cluster_name`) to maintain consistency. Also, avoid using paths when importing roles as indicated by the pipeline.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 35-35: Avoid using paths when importing roles. (infrastructure/ingress)

---

[warning] 37-37: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 38-38: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 39-39: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 40-40: Variables names from within roles should use ingress_ as a prefix. (vars: hosted_cluster_state)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T14:41:29Z
author_association: NONE
diff_hunk: @@ -0,0 +1,25 @@
+- name: wait for hostedcluster to finish deleting
+  kubernetes.core.k8s_info:
+    api_version: hypershift.openshift.io/v1beta1
+    kind: HostedCluster
+    namespace: "{{ hosted_cluster_namespace }}"
+    name: "{{ hosted_cluster_name }}"
+  register: hosted_cluster_check
+  until: not hosted_cluster_check.resources
+  retries: 360
+  delay: 5
+
+- name: destroy api floating ip
+  include_role:
+    name: infrastructure
+    tasks_from: destroy_floating_ip.yaml
+  vars:
+    floating_ip_name: "{{ hosted_cluster_name }}-api"
path: roles/infrastructure/api/tasks/destroy_api_infra.yaml
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Apply Variable Prefix for API Floating IP.**  
The variable `floating_ip_name` being passed should follow the naming convention. Consider renaming it to `infrastructure_floating_ip_name` as per the pipeline warning:  
```diff
-    floating_ip_name: "{{ hosted_cluster_name }}-api"
+    infrastructure_floating_ip_name: "{{ hosted_cluster_name }}-api"
```  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
  vars:
-    floating_ip_name: "{{ hosted_cluster_name }}-api"
+    infrastructure_floating_ip_name: "{{ hosted_cluster_name }}-api"
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 17-17: Variables names from within roles should use infrastructure_ as a prefix. (vars: floating_ip_name)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T14:41:29Z
author_association: NONE
diff_hunk: @@ -0,0 +1,25 @@
+- name: wait for hostedcluster to finish deleting
+  kubernetes.core.k8s_info:
+    api_version: hypershift.openshift.io/v1beta1
+    kind: HostedCluster
+    namespace: "{{ hosted_cluster_namespace }}"
+    name: "{{ hosted_cluster_name }}"
+  register: hosted_cluster_check
+  until: not hosted_cluster_check.resources
+  retries: 360
+  delay: 5
+
+- name: destroy api floating ip
+  include_role:
+    name: infrastructure
+    tasks_from: destroy_floating_ip.yaml
+  vars:
path: roles/infrastructure/api/tasks/destroy_api_infra.yaml
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Use Fully Qualified Collection Name for include_role.**  
Pipeline warnings indicate that the built-in module actions should be referenced using their FQCN. Replace the plain `include_role` with:  
```diff
-  include_role:
-    name: infrastructure
-    tasks_from: destroy_floating_ip.yaml
+  ansible.builtin.include_role:
+    name: infrastructure
+    tasks_from: destroy_floating_ip.yaml
```  
This ensures clarity and consistency in role imports.  



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
- name: destroy api floating ip
  ansible.builtin.include_role:
    name: infrastructure
    tasks_from: destroy_floating_ip.yaml
  vars:
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 12-12: Use FQCN for builtin module actions (include_role).

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit d76ed28
created_at: 2025-04-09T14:41:30Z
author_association: NONE
diff_hunk: @@ -0,0 +1,54 @@
+- name: get kube-apiserver service
+  kubernetes.core.k8s_info:
+    api_version: v1
+    kind: Service
+    namespace: "{{ hosted_cluster_namespace }}-{{ hosted_cluster_name }}"
+    name: kube-apiserver
+  register: kube_apiserver_service
+
+  # wait until the service exists and has a loadbalancer ip
+  until: >-
+    kube_apiserver_service.resources and
+    kube_apiserver_service.resources[0].status.loadBalancer.ingress|default(false)
+  retries: "{{ kube_apiserver_wait_retries|default(resource_wait_retries) }}"
+  delay: "{{ kube_apiserver_wait_delay|default(resource_wait_delay) }}"
+
+- name: set api_internal_ip
+  set_fact:
+    api_internal_ip: "{{ kube_apiserver_service.resources[0].status.loadBalancer.ingress[0].ip }}"
+
+- name: allocate api floating ip
+  include_role:
+    name: infrastructure
+    tasks_from: allocate_floating_ip
+  vars:
+    floating_ip_name: "{{ hosted_cluster_name }}-api"
+
path: roles/infrastructure/api/tasks/create_api_infra.yaml
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Floating IP Allocation for API**

The inclusion of the floating IP allocation task via `include_role` is a good modular approach. However, the variable `floating_ip_name` is derived from `hosted_cluster_name` with an appended `-api`. To align with role-specific naming conventions, consider renaming it to something like `api_floating_ip_name`.


```diff
-  vars:
-    floating_ip_name: "{{ hosted_cluster_name }}-api"
+  vars:
+    api_floating_ip_name: "{{ hosted_cluster_name }}-api"
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
- name: allocate api floating ip
  include_role:
    name: infrastructure
    tasks_from: allocate_floating_ip
  vars:
    api_floating_ip_name: "{{ hosted_cluster_name }}-api"
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 20-20: Use FQCN for builtin module actions (include_role).

---

[warning] 20-20: All names should start with an uppercase letter.

---

[warning] 25-25: Variables names from within roles should use infrastructure_ as a prefix. (vars: floating_ip_name)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T14:41:30Z
author_association: NONE
diff_hunk: @@ -0,0 +1,54 @@
+- name: wait until agents are available
+  kubernetes.core.k8s_info:
+    api_version: agent-install.openshift.io/v1beta1
+    kind: Agent
+    namespace: "hardware-inventory"
+    label_selectors:
+    - "cloudkit.openshift.io/clusterorder={{ hosted_cluster_name }}"
+  register: agents
+
+  # wait until agents are assigned to the cluster
+  until: agents.resources
+  retries: "{{ agent_wait_retries|default(resource_wait_retries) }}"
+  delay: "{{ agent_wait_delay|default(resource_wait_delay) }}"
+
+- name: set worker_node_ip
+  set_fact:
+    worker_node_ip: >-
+      {{
+      agents|
+      json_query('resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]')|
+      shuffle|
+      first|
+      ansible.utils.ipaddr('address')
+      }}
+
+- name: allocate ingress floating ip
+  include_role:
+    name: infrastructure
+    tasks_from: allocate_floating_ip.yaml
+  vars:
+    floating_ip_name: "{{ hosted_cluster_name }}-ingress"
+
path: roles/infrastructure/ingress/tasks/create_ingress_infra.yaml
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Ingress Floating IP Allocation**

This task’s inclusion of the floating IP allocation role is structured similarly to the API task. However, the variable `floating_ip_name` should be adjusted to follow an ingress-specific naming convention (e.g. `ingress_floating_ip_name`).


```diff
-    floating_ip_name: "{{ hosted_cluster_name }}-ingress"
+    ingress_floating_ip_name: "{{ hosted_cluster_name }}-ingress"
```


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
- name: allocate ingress floating ip
  include_role:
    name: infrastructure
    tasks_from: allocate_floating_ip.yaml
  vars:
    ingress_floating_ip_name: "{{ hosted_cluster_name }}-ingress"
`````

</details>

<!-- suggestion_end -->

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit 1357961
created_at: 2025-04-09T14:41:30Z
author_association: NONE
diff_hunk: @@ -14,11 +16,25 @@
     - role: cluster_settings
       vars:
         cluster_settings_template_id: "{{ cluster_order.spec.templateID }}"
-        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters }}"
+        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters | from_json }}"
 
     - role: hosted_cluster
       vars:
         hosted_cluster_name: "{{ cluster_order.metadata.name }}"
         hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
         hosted_cluster_settings: "{{ cluster_settings_result }}"
         hosted_cluster_state: present
+
+    - role: infrastructure/api
+      vars:
+        hosted_cluster_name: "{{ cluster_order.metadata.name }}"
+        hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
+        hosted_cluster_settings: "{{ cluster_settings_result }}"
+        hosted_cluster_state: present
+
path: playbook_cloudkit_create_hosted_cluster.yml
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**API Role Variable Naming**

To comply with naming conventions and pipeline requirements, update the variables passed to the `infrastructure/api` role with an `api_` prefix:
```diff
-    hosted_cluster_name: "{{ cluster_order.metadata.name }}"
-    hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
-    hosted_cluster_settings: "{{ cluster_settings_result }}"
-    hosted_cluster_state: present
+    api_hosted_cluster_name: "{{ cluster_order.metadata.name }}"
+    api_hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
+    api_hosted_cluster_settings: "{{ cluster_settings_result }}"
+    api_hosted_cluster_state: present
```



<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

`````suggestion
    - role: infrastructure/api
      vars:
        api_hosted_cluster_name: "{{ cluster_order.metadata.name }}"
        api_hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
        api_hosted_cluster_settings: "{{ cluster_settings_result }}"
        api_hosted_cluster_state: present
`````

</details>

<!-- suggestion_end -->

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 GitHub Check: ansible-lint / Ansible Lint</summary>

[failure] 33-33: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

---

[failure] 32-32: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[failure] 31-31: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[failure] 30-30: var-naming[no-role-prefix]
Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

---

[failure] 28-28: role-name[path]
Avoid using paths when importing roles. (infrastructure/api)

</details>
<details>
<summary>🪛 GitHub Actions: CI</summary>

[warning] 28-28: Avoid using paths when importing roles. (infrastructure/api)

---

[warning] 30-30: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_name)

---

[warning] 31-31: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_namespace)

---

[warning] 32-32: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_settings)

---

[warning] 33-33: Variables names from within roles should use api_ as a prefix. (vars: hosted_cluster_state)

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T14:58:18Z
author_association: NONE
diff_hunk: @@ -0,0 +1,54 @@
+- name: Get kube-apiserver service
+  kubernetes.core.k8s_info:
+    api_version: v1
+    kind: Service
+    namespace: "{{ hosted_cluster_namespace }}-{{ hosted_cluster_name }}"
+    name: Kube-apiserver
+  register: kube_apiserver_service
+
+  # wait until the service exists and has a loadbalancer ip
+  until: >-
+    kube_apiserver_service.resources and
+    kube_apiserver_service.resources[0].status.loadBalancer.ingress | default(false)
+  retries: "{{ kube_apiserver_wait_retries | default(resource_wait_retries) }}"
+  delay: "{{ kube_apiserver_wait_delay | default(resource_wait_delay) }}"
+
+- name: Set api_internal_ip
+  ansible.builtin.set_fact:
+    api_internal_ip: "{{ kube_apiserver_service.resources[0].status.loadBalancer.ingress[0].ip }}"
+
+- name: Allocate api floating ip
+  ansible.builtin.include_role:
+    name: Infrastructure
+    tasks_from: allocate_floating_ip
+  vars:
+    floating_ip_name: "{{ hosted_cluster_name }}-api"
+
+- name: Set api_floating_ip
+  ansible.builtin.set_fact:
+    api_floating_ip: "{{ allocate_floating_ip_result }}"
+
+# FIXME: This will fail erroneously because the command is not idempotent;
+# see https://github.com/CCI-MOC/python-esiclient/issues/82. Unfortunately,
+# it will also fail legitimately if we failed to clean up from a previous
+# cluster and there exists a port forwarding to the same internal ip
+# address.
+- name: Create port forwarding
+  ansible.builtin.command: >-
+    openstack esi port forwarding create {{ api_internal_ip }} {{ api_floating_ip }}
+      --internal-ip-network "{{ api_internal_ip_network }}"
+      -p {{ api_kube_apiserver_port }} -d "{{ hosted_cluster_name }}" -f json
+  register: create_forwarding
+  failed_when: >-
+    create_forwarding.rc != 0 and "duplicate port forwarding" not in create_forwarding.stderr
+
path: roles/infrastructure/api/tasks/create_api_infra.yaml
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Idempotency Concern in Port Forwarding Command**  
The port forwarding command is noted as non-idempotent (see FIXME comment). It risks failing both due to duplicate forwarding and actual issues if previous resources haven’t been cleaned up. Consider either adding a pre-check for existing port forwarding rules or using an idempotent module if one exists.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T15:08:05Z
author_association: NONE
diff_hunk: @@ -0,0 +1,54 @@
+- name: Wait until agents are available
+  kubernetes.core.k8s_info:
+    api_version: agent-install.openshift.io/v1beta1
+    kind: Agent
+    namespace: "hardware-inventory"
+    label_selectors:
+    - "cloudkit.openshift.io/clusterorder={{ hosted_cluster_name }}"
+  register: agents
+
+  # wait until agents are assigned to the cluster
+  until: agents.resources
+  retries: "{{ agent_wait_retries | default(resource_wait_retries) }}"
+  delay: "{{ agent_wait_delay | default(resource_wait_delay) }}"
+
+- name: Set worker_node_ip
+  ansible.builtin.set_fact:
+    worker_node_ip: >-
+      {{
+      agents |
+      json_query('resources[*].status.inventory.interfaces[?ipV4Addresses][].ipV4Addresses[]') |
+      shuffle |
+      first |
+      ansible.utils.ipaddr('address')
+      }}
+
+- name: Allocate ingress floating ip
+  ansible.builtin.include_role:
+    name: Infrastructure
+    tasks_from: allocate_floating_ip.yaml
+  vars:
+    floating_ip_name: "{{ hosted_cluster_name }}-ingress"
+
+- name: Set ingress_floating_ip
+  ansible.builtin.set_fact:
+    ingress_floating_ip: "{{ allocate_floating_ip_result }}"
+
+- name: Create port forwarding for ingress
+  ansible.builtin.command: >-
+    openstack esi port forwarding create {{ worker_node_ip }} {{ ingress_floating_ip }}
+      -p 80 -p 443 -d "{{ hosted_cluster_name }}-ingress" -f json
+  register: create_forwarding
+  failed_when: >-
+    create_forwarding.rc != 0 and "duplicate port forwarding" not in create_forwarding.stderr
+
path: roles/infrastructure/ingress/tasks/create_ingress_infra.yaml
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Ingress Port Forwarding Idempotency Concern**  
Much like the API port forwarding task, the ingress port forwarding command may fail if a duplicate exists. Consider enhancing the task with a pre-check or employing an idempotent solution to manage port forwarding reliably.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-04-09T15:08:05Z
author_association: NONE
diff_hunk: @@ -14,11 +16,25 @@
     - role: cluster_settings
       vars:
         cluster_settings_template_id: "{{ cluster_order.spec.templateID }}"
-        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters }}"
+        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters | from_json }}"
 
     - role: hosted_cluster
       vars:
         hosted_cluster_name: "{{ cluster_order.metadata.name }}"
         hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
         hosted_cluster_settings: "{{ cluster_settings_result }}"
         hosted_cluster_state: present
+
+    - role: infrastructure/api
path: playbook_cloudkit_create_hosted_cluster.yml
user: login: adriengentil
body: generally, `ansible-lint` doesn't really like nested roles. Maybe the solution would be to split the roles in collections, e.g.: 
- `collections/cloudkit/hosted_cluster/roles/...`
- `collection/cloudlkit/infrastructure/roles/...`

It failed last time I tried, and I did not look for a solution, but I could be a better way to organize our stuff
created_at: 2025-04-09T16:00:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -14,11 +16,25 @@
     - role: cluster_settings
       vars:
         cluster_settings_template_id: "{{ cluster_order.spec.templateID }}"
-        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters }}"
+        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters | from_json }}"
 
     - role: hosted_cluster
       vars:
         hosted_cluster_name: "{{ cluster_order.metadata.name }}"
         hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
         hosted_cluster_settings: "{{ cluster_settings_result }}"
         hosted_cluster_state: present
+
+    - role: infrastructure/api
path: playbook_cloudkit_create_hosted_cluster.yml
user: login: larsks
body: I have configured ansible-lint to disable the `role-name[path]` rule for now. I've updated everything to either fix the linting errors or to add `noqa:...` comments where I disagree with `ansible-lint`.
created_at: 2025-04-11T02:04:44Z
author_association: MEMBER
diff_hunk: @@ -14,11 +16,25 @@
     - role: cluster_settings
       vars:
         cluster_settings_template_id: "{{ cluster_order.spec.templateID }}"
-        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters }}"
+        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters | from_json }}"
 
     - role: hosted_cluster
       vars:
         hosted_cluster_name: "{{ cluster_order.metadata.name }}"
         hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
         hosted_cluster_settings: "{{ cluster_settings_result }}"
         hosted_cluster_state: present
+
+    - role: infrastructure/api
path: playbook_cloudkit_create_hosted_cluster.yml
user: login: adriengentil
body: The more I think about it, the more I think we should avoid nested roles as give me a hard time to understand how it works, and I think goes against the simplicity that ansible is supposed to bring. 

I think we should strive to keep the roles flat in a flat structure and compose them. If the issue is a problem of namespace, then I think  that grouping stuff into collections should be the way. 

I'm ok to merge this PR as it is, and in the meantime, I'll look at how we can organize our roles into collections. So we can move this infrastructure role in a dedicated place.

What do you think?
created_at: 2025-04-11T08:59:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -14,11 +16,25 @@
     - role: cluster_settings
       vars:
         cluster_settings_template_id: "{{ cluster_order.spec.templateID }}"
-        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters }}"
+        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters | from_json }}"
 
     - role: hosted_cluster
       vars:
         hosted_cluster_name: "{{ cluster_order.metadata.name }}"
         hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
         hosted_cluster_settings: "{{ cluster_settings_result }}"
         hosted_cluster_state: present
+
+    - role: infrastructure/api
path: playbook_cloudkit_create_hosted_cluster.yml
user: login: larsks
body: Sure, let's merge this and then we can work on restructuring it.
created_at: 2025-04-11T20:27:01Z
author_association: MEMBER
diff_hunk: @@ -14,11 +16,25 @@
     - role: cluster_settings
       vars:
         cluster_settings_template_id: "{{ cluster_order.spec.templateID }}"
-        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters }}"
+        cluster_settings_template_parameters: "{{ cluster_order.spec.templateParameters | from_json }}"
 
     - role: hosted_cluster
       vars:
         hosted_cluster_name: "{{ cluster_order.metadata.name }}"
         hosted_cluster_namespace: "{{ cluster_working_namespace_result }}"
         hosted_cluster_settings: "{{ cluster_settings_result }}"
         hosted_cluster_state: present
+
+    - role: infrastructure/api
path: playbook_cloudkit_create_hosted_cluster.yml
user: login: adriengentil
body: Created https://github.com/innabox/cloudkit-aap/issues/18 to track it
created_at: 2025-04-14T08:10:37Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-09T14:41:26Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
This pull request introduces multiple updates across configuration, playbooks, dependencies, and roles. In the collections requirements file, four new collections are added with specified versions. Both cluster creation and deletion playbooks are modified by adding an `OS_CLOUD` environment variable, adjusting parameter parsing, and incorporating new roles related to API and ingress management. The Python project’s dependency file now includes additional packages for AWS and OpenStack support. In the infrastructure roles, new configuration parameters and defaults are set for network settings and DNS management. Additionally, several new task files manage the creation and destruction of API and ingress infrastructure, including conditional task inclusion based on the `hosted_cluster_state`. Tasks for allocating and destroying floating IPs have been introduced, along with new dependencies between roles to ensure proper sequencing of operations.

## Suggested reviewers
- mhrivnak

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!TIP]
> <details>
> <summary>⚡💬 Agentic Chat (Pro Plan, General Availability)</summary>
> 
> - We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
> 
> </details>

<!-- announcements_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=innabox/cloudkit-aap&utm_content=15):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: adriengentil
created_at: 2025-04-14T08:13:25Z
body: @larsks ansible-lint complains about:
```
couldn't resolve module/action 'kubernetes.core.k8s'. This often indicates a misspelling, missing collection, or incorrect module path.
````

because we removed `collections/requirements.yml`. ansible-lint uses this file to resolve the dependencies. Maybe it's better to restore it? 
In the future, we would likely want to run ansible-lint from the EE image.
author_association: CONTRIBUTOR
user: login: larsks
created_at: 2025-04-14T13:43:52Z
body: > because we removed collections/requirements.yml

Not intentionally. I changed it into a symlink, but apparently forgot to include it in a commit. Pushing an update with that fix.
author_association: MEMBER