Skip to content

Commit 50b21c1

Browse files
authored
feat(role): expose publishing.externalIPs and tenant-root ingress via role variables (#30)
* feat(role): add cozystack_external_ips for publishing.externalIPs Add a new list variable cozystack_external_ips that renders into publishing.externalIPs in the Platform Package template. Required on platforms without a native load balancer (cloud VMs, bare metal) running the isp-full-generic variant. Includes IP validation via the existing is_ip_address Jinja2 test, a dedicated validate-external-ips.yml task file, and a fix for existing tests to define the new variable in pre_tasks. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> * feat(role): add cozystack_tenant_root_ingress for root tenant ingress Add a boolean variable cozystack_tenant_root_ingress (default false) that patches the root Tenant CR to set spec.ingress=true after the Platform Package is applied. This creates the tenant-root IngressClass and deploys the ingress-nginx controller pods. Waits for the root Tenant to exist (retries with backoff) since Platform Package triggers tenant creation asynchronously. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> * test: add external IPs validation and rendering tests Add test playbook that verifies externalIPs renders correctly in the Platform Package template when IPs are provided and is omitted when the list is empty. Add test inventories for valid and invalid IP inputs and a CI workflow job that runs both. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> * docs: document external IPs and tenant ingress variables Add cozystack_external_ips and cozystack_tenant_root_ingress to the README optional variables table, CHANGELOG Unreleased section, and all three example inventories (commented out). Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la> --------- Signed-off-by: Aleksei Sviridkin <f@lex.la>
1 parent ec876ae commit 50b21c1

14 files changed

Lines changed: 312 additions & 0 deletions

File tree

.github/workflows/test.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,52 @@ jobs:
129129
130130
echo "OK: Hostname host keys correctly rejected"
131131
132+
external-ips:
133+
name: External IPs validation
134+
runs-on: ubuntu-latest
135+
steps:
136+
- name: Checkout
137+
uses: actions/checkout@v6
138+
139+
- name: Set up Python
140+
uses: actions/setup-python@v6
141+
with:
142+
python-version: "3.14"
143+
144+
- name: Install Ansible
145+
run: pip install ansible-core
146+
147+
- name: Build and install collection
148+
run: |
149+
ansible-galaxy collection build
150+
ansible-galaxy collection install cozystack-installer-*.tar.gz --force
151+
152+
- name: Test external IPs rendering
153+
run: >-
154+
ansible-playbook tests/test-external-ips.yml
155+
--inventory tests/test-external-ips-inventory.yml
156+
157+
- name: Test invalid IPs are rejected
158+
run: |
159+
set +e
160+
output="$(ansible-playbook tests/test-external-ips.yml \
161+
--inventory tests/test-invalid-ips-inventory.yml 2>&1)"
162+
status=$?
163+
set -e
164+
165+
if [ "$status" -eq 0 ]; then
166+
echo "ERROR: Expected failure for invalid IPs, but playbook succeeded"
167+
exit 1
168+
fi
169+
170+
if ! grep -q "not a valid IP address in cozystack_external_ips" <<< "$output"; then
171+
echo "ERROR: Playbook failed, but not due to IP validation"
172+
echo "$output"
173+
exit 1
174+
fi
175+
176+
echo "OK: Invalid IPs correctly rejected"
177+
132178
e2e:
133179
name: E2E
134180
runs-on: ubuntu-latest

CHANGELOG.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ Synced with Cozystack v1.1.3.
3838
Unreleased
3939
==========
4040

41+
- New variable ``cozystack_external_ips`` (list, default ``[]``): external
42+
IP addresses for ingress-nginx Service ``externalIPs``. Required on
43+
``isp-full-generic`` platform variant when nodes lack a native load
44+
balancer (cloud VMs, bare metal).
45+
- New variable ``cozystack_tenant_root_ingress`` (bool, default ``false``):
46+
when enabled, patches the root Tenant CR to set ``spec.ingress: true``
47+
after Platform Package apply, creating the ``tenant-root`` IngressClass
48+
and ingress-nginx controller pods.
49+
4150
Node prerequisites: comprehensive audit and install in examples.
4251

4352
- Example prepare playbooks now install the full set of node prerequisites.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,8 @@ Runs on `server[0]` only.
322322
| `cozystack_create_platform_package` | `true` | Create Platform Package CR after install |
323323
| `cozystack_platform_variant` | `isp-full-generic` | Platform variant: default, isp-full, isp-hosted, isp-full-generic |
324324
| `cozystack_root_host` | `""` | Domain for Cozystack services (empty = skip publishing) |
325+
| `cozystack_external_ips` | `[]` | List of external IPs for ingress-nginx Service. Required on platforms without a native LB (cloud VMs, bare metal). Each entry must be a valid IPv4/IPv6 address. |
326+
| `cozystack_tenant_root_ingress` | `false` | Enable ingress on the root tenant. When `true`, patches the root Tenant CR after Platform Package apply to create IngressClass and ingress-nginx controller. |
325327
| `cozystack_pod_cidr` | `10.42.0.0/16` | Pod CIDR for Platform Package |
326328
| `cozystack_pod_gateway` | `10.42.0.1` | Pod gateway |
327329
| `cozystack_svc_cidr` | `10.43.0.0/16` | Service CIDR |

examples/rhel/inventory.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ cluster:
3131
cozystack_root_host: "cozy.example.com"
3232
cozystack_platform_variant: "isp-full-generic"
3333

34+
# External IPs for ingress-nginx (required on cloud VMs without native LB)
35+
# cozystack_external_ips:
36+
# - "203.0.113.10"
37+
38+
# Enable ingress on root tenant (creates IngressClass + controller)
39+
# cozystack_tenant_root_ingress: true
40+
3441
# Extra k3s flags (used in prepare-rhel.yml)
3542
cozystack_k3s_extra_args: "--tls-san=203.0.113.10"
3643

examples/suse/inventory.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ cluster:
3131
cozystack_root_host: "cozy.example.com"
3232
cozystack_platform_variant: "isp-full-generic"
3333

34+
# External IPs for ingress-nginx (required on cloud VMs without native LB)
35+
# cozystack_external_ips:
36+
# - "203.0.113.10"
37+
38+
# Enable ingress on root tenant (creates IngressClass + controller)
39+
# cozystack_tenant_root_ingress: true
40+
3441
# Extra k3s flags (used in prepare-suse.yml)
3542
cozystack_k3s_extra_args: "--tls-san=203.0.113.10"
3643

examples/ubuntu/inventory.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ cluster:
3838
cozystack_root_host: "cozy.example.com"
3939
cozystack_platform_variant: "isp-full-generic"
4040

41+
# External IPs for ingress-nginx (required on cloud VMs without native LB)
42+
# cozystack_external_ips:
43+
# - "203.0.113.10"
44+
45+
# Enable ingress on root tenant (creates IngressClass + controller)
46+
# cozystack_tenant_root_ingress: true
47+
4148
# Extra k3s flags (used in prepare-ubuntu.yml)
4249
cozystack_k3s_extra_args: "--tls-san=203.0.113.10"
4350

roles/cozystack/defaults/main.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ cozystack_platform_variant: "isp-full-generic"
5353
# Leave empty to skip publishing config in the Platform Package
5454
cozystack_root_host: ""
5555

56+
# External IPs for ingress-nginx Service (type: LoadBalancer).
57+
# Required on platforms without a native LB (cloud VMs, bare metal).
58+
# Each entry must be a valid IPv4 or IPv6 address.
59+
cozystack_external_ips: []
60+
61+
# Enable ingress on the root tenant (tenant-root).
62+
# When true, patches the root Tenant CR after Platform Package apply
63+
# so that IngressClass and ingress-nginx controller are created.
64+
cozystack_tenant_root_ingress: false
65+
5666
# Comma-separated control-plane node IPs for kube-ovn RAFT consensus.
5767
# Empty = auto-detect from 'server' inventory group host keys.
5868
cozystack_master_nodes: ""

roles/cozystack/tasks/main.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313
ansible.builtin.include_tasks: compute-master-nodes.yml
1414
when: cozystack_create_platform_package
1515

16+
- name: Validate external IP addresses
17+
ansible.builtin.include_tasks: validate-external-ips.yml
18+
when:
19+
- cozystack_create_platform_package
20+
- cozystack_external_ips | length > 0
21+
1622
- name: Set architecture for Helm download
1723
ansible.builtin.set_fact:
1824
_cozystack_arch: "{{ 'amd64' if ansible_architecture == 'x86_64' else 'arm64' }}"
@@ -147,3 +153,38 @@
147153
become: true
148154
changed_when: false
149155
when: cozystack_create_platform_package
156+
157+
- name: Wait for root Tenant to exist
158+
ansible.builtin.command:
159+
cmd: >-
160+
kubectl --kubeconfig {{ cozystack_kubeconfig }}
161+
wait tenants.apps.cozystack.io/root
162+
--namespace tenant-root
163+
--for=jsonpath='{.metadata.name}'=root
164+
--timeout={{ cozystack_operator_wait_timeout }}s
165+
become: true
166+
changed_when: false
167+
register: _tenant_wait
168+
retries: 5
169+
delay: 15
170+
until: _tenant_wait.rc == 0
171+
when:
172+
- cozystack_tenant_root_ingress
173+
- cozystack_create_platform_package
174+
- not ansible_check_mode
175+
176+
- name: Enable ingress on root Tenant
177+
ansible.builtin.command:
178+
cmd: >-
179+
kubectl --kubeconfig {{ cozystack_kubeconfig }}
180+
patch tenants.apps.cozystack.io root
181+
--namespace tenant-root
182+
--type=merge
183+
--patch '{"spec":{"ingress":true}}'
184+
become: true
185+
register: _tenant_patch
186+
changed_when: "'patched' in _tenant_patch.stdout and '(no change)' not in _tenant_patch.stdout"
187+
when:
188+
- cozystack_tenant_root_ingress
189+
- cozystack_create_platform_package
190+
- not ansible_check_mode
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
# Validate that each entry in cozystack_external_ips is a valid IP address.
3+
# Included from main.yml and tests.
4+
- name: Validate cozystack_external_ips is a list
5+
ansible.builtin.assert:
6+
that:
7+
- cozystack_external_ips is sequence
8+
- cozystack_external_ips is not string
9+
- cozystack_external_ips is not mapping
10+
fail_msg: >-
11+
cozystack_external_ips must be a list of IP address strings.
12+
Got type {{ cozystack_external_ips | type_debug }}.
13+
14+
- name: Validate each external IP is a valid address
15+
ansible.builtin.assert:
16+
that:
17+
- item is cozystack.installer.is_ip_address
18+
fail_msg: >-
19+
'{{ item }}' is not a valid IP address in cozystack_external_ips.
20+
Each entry must be a valid IPv4 or IPv6 address.
21+
loop: "{{ cozystack_external_ips }}"
22+
when: cozystack_external_ips | length > 0

roles/cozystack/templates/platform-package.yml.j2

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,7 @@ spec:
2828
publishing:
2929
host: {{ cozystack_root_host | to_json }}
3030
apiServerEndpoint: {{ _cozystack_api_endpoint | to_json }}
31+
{% if cozystack_external_ips | length > 0 %}
32+
externalIPs: {{ cozystack_external_ips | to_json }}
33+
{% endif %}
3134
{% endif %}

0 commit comments

Comments
 (0)