mirror of
https://github.com/techno-tim/k3s-ansible.git
synced 2026-08-08 23:13:19 +02:00
bb006cf157
* fix(calico): support split CRDs for current releases - Download the v1_crd_projectcalico_org.yaml bundle before the operator - Apply both files with server-side apply and force-conflicts per the upstream upgrade procedure - Wait for the operator Deployment and for the managed CRDs to be Established after the operator starts - Replace the create/rescue/replace flow with an idempotent apply that no longer conceals partial failures - Verify TigeraStatus for calico and apiserver is Available, not just that Pods exist * feat(dependencies): upgrade supported cluster components - Bump K3s to v1.36.2+k3s1, Calico to v3.32.1, Cilium to v1.20.0, kube-vip to v1.2.2, kube-vip cloud provider to v0.0.12, and MetalLB to v0.16.0 across sample inventory, role defaults, and argument specs - Pin the Cilium CLI with a new cilium_cli_tag (v0.19.7) instead of the floating stable.txt lookup - Replace the CiliumBGPPeeringPolicy v2alpha1 BGP template with the v2 CiliumBGPClusterConfig, CiliumBGPPeerConfig, CiliumBGPAdvertisement, and CiliumLoadBalancerIPPool resource set - Move Cilium load balancer Helm keys from bpf.loadBalancer to the valid top-level loadBalancer path - Add preflight schema validation and remove the deprecated policy after the v2 objects are accepted - Wait for cilium status after installation - Pin kube-vip RBAC in a repository template instead of fetching a mutable URL, and include EndpointSlice permissions - Fix the kube-vip bgppeers format to address:ASN comma-separated peers - Fail clearly when the MetalLB speaker tag replacement does not apply - Drop the obsolete MetalLB webhook service name version branch * test(molecule): verify upgraded cluster components - Assert every node reports the expected K3s kubelet version - Verify the active CNI (Flannel / Calico / Cilium) is Ready and runs the expected image tag, including Calico TigeraStatus Available - Verify the active load balancer (MetalLB / kube-vip) runs the expected image tags and that MetalLB is absent when kube-vip is active - Assert no Flannel DaemonSet remains when Calico or Cilium is enabled - Assert the example LoadBalancer address falls inside the configured pool range - Add a manifest-only Cilium BGP regression test that renders the v2 template with zero, one, and multiple neighbors and rejects any v2alpha1 or CiliumBGPPeeringPolicy output * fix(dependencies): correct dependency version pins - Set the sample kube-vip image to v1.2.2 and repair the damaged comment - Pin the kube-vip cloud provider default to v0.0.12 in the task URL - Set the MetalLB controller argument-spec default to v0.16.0 - Restore the MetalLB available timeout default to 240s * docs(dependencies): document current cluster versions - Update kube-vip, kube-vip cloud provider, and MetalLB defaults - Add cilium_tag and cilium_cli_tag rows - Explain that MetalLB v0.16.0 is the application image target even though a newer chart-only tag (metallb-chart-0.16.1) exists - Add an existing-cluster upgrade warning covering the K3s etcd 3.5.26 bridge and one-minor-at-a-time rule, consecutive Cilium minor upgrades, Calico v3 resource UID handling, and MetalLB app vs chart tags * fix(dependencies): address PR review findings - Read the MetalLB speaker tag check from the managed host with slurp instead of a controller-side file lookup, and match the full image reference - Restore the tigera-operator namespace on the Calico operator Deployment wait while keeping the managed CRD waits cluster-scoped - Make Molecule verify inputs durable and scenario-specific via a per-scenario verify-vars.yml, driven by explicit verify_cni/verify_lb values instead of non-persisted converge facts - Rename the kube-vip multi-peer BGP env var from bgppeers to bgp_peers and vip_cidr to vip_subnet so v1.2.2 actually reads them - Map the legacy Cilium routed mode to tunnel and stop passing the alias directly to the chart - Use return-code based failed_when on apply and preflight commands so non-error failures are no longer treated as success - Clarify the sequential K3s upgrade path and backups in the README - Add kube-vip and MetalLB regression tests and a Cilium mode mapping unit * fix(dependencies): resolve re-review findings - correct the Calico TigeraStatus resource kind\n- document tunnel as the supported Cilium routing mode\n- validate load balancer addresses across range and CIDR pools * fix(molecule): verify embedded flannel instead of a flannel DaemonSet - K3s 1.36 runs flannel embedded in the k3s agent rather than as a kube-flannel-ds DaemonSet, so the flannel verifier queried a workload that no longer exists and failed the verify step - For the flannel scenarios, assert every node is Ready and that neither the Calico nor the Cilium namespace exists - Drop the now-invalid kube-flannel-ds DaemonSet assertion * fix(molecule): wait for the LoadBalancer address before asserting reachability - The nginx LoadBalancer service had no ingress address when the reachability assertion ran, so status.loadBalancer.ingress[0].ip was undefined and the ipwrap filter failed during verify - Poll the service until MetalLB or kube-vip assigns an external IP - Record the assigned address once and reuse it for the reachability probe and the pool membership checks * fix(ci): harden calico apiserver wait and extend molecule job timeout - Bump calico system resources wait retries 30->60 and delay 7->10 so the slow-to-reconcile calico-apiserver deployment has enough time under nested-virt - Raise the molecule step timeout-minutes from 90 to 150 to accommodate contended 5-node scenarios (cilium, kube-vip) that were hitting the 90-min cap * fix(calico): treat optional API server as best-effort on converge - The Calico API server (calico-apiserver) is an optional add-on for managing Calico policy through the projectcalico.org/v3 Kubernetes API; it is not required for Calico CNI data plane operation - With Calico v3.32.1 on K3s 1.36 the tigera-operator never provisions the calico-apiserver namespace, causing the converge wait to fail deterministically - Keep the strict wait for core Calico components (typha, kube-controllers, calico-node, csi-node-driver) and make the API server wait tolerate failure - Restrict the TigeraStatus Available check to the calico status, matching the upstream v3.32.1 K3s quickstart which validates without the API server
314 lines
13 KiB
YAML
314 lines
13 KiB
YAML
---
|
|
# Scenario-aware verification of cluster components and their live image tags.
|
|
# Scenario identity (verify_cni / verify_lb) and expected address range come
|
|
# from each scenario's verify-vars.yml, which is plain inventory data available
|
|
# to the verify play. Converge-time set_fact values are not persisted between
|
|
# the two Ansible processes, so they are never used here.
|
|
- name: Verify cluster components report expected versions
|
|
block:
|
|
- name: Get all nodes with their kubelet versions
|
|
kubernetes.core.k8s_info:
|
|
kind: node
|
|
kubeconfig: "{{ kubecfg_path }}"
|
|
register: verify_nodes
|
|
|
|
- name: Assert each node reports the expected Kubernetes version
|
|
ansible.builtin.assert:
|
|
that: item.status.nodeInfo.kubeletVersion == k3s_version
|
|
success_msg: "{{ item.metadata.name }} reports {{ k3s_version }}"
|
|
fail_msg: >-
|
|
{{ item.metadata.name }} reports
|
|
{{ item.status.nodeInfo.kubeletVersion }},
|
|
expected {{ k3s_version }}
|
|
loop: "{{ verify_nodes.resources }}"
|
|
loop_control:
|
|
label: "{{ item.metadata.name }}"
|
|
|
|
- name: Verify Flannel is the active CNI
|
|
when: verify_cni == 'flannel'
|
|
block:
|
|
- name: Assert every node reports Ready
|
|
ansible.builtin.assert:
|
|
that: item.status.conditions
|
|
| selectattr('type', 'equalto', 'Ready')
|
|
| map(attribute='status') | first | default('') == 'True'
|
|
success_msg: "{{ item.metadata.name }} is Ready"
|
|
fail_msg: "{{ item.metadata.name }} is not Ready"
|
|
loop: "{{ verify_nodes.resources }}"
|
|
loop_control:
|
|
label: "{{ item.metadata.name }} ready"
|
|
|
|
- name: Get any Calico namespaces with Flannel enabled
|
|
kubernetes.core.k8s_info:
|
|
kind: Namespace
|
|
name: calico-system
|
|
kubeconfig: "{{ kubecfg_path }}"
|
|
register: flannel_calico_absent
|
|
|
|
- name: Assert there is no Calico system namespace
|
|
ansible.builtin.assert:
|
|
that: flannel_calico_absent.resources | length == 0
|
|
success_msg: "No Calico present with Flannel"
|
|
fail_msg: "A Calico namespace exists alongside Flannel"
|
|
|
|
- name: Get the Cilium namespace with Flannel enabled
|
|
kubernetes.core.k8s_info:
|
|
kind: Namespace
|
|
name: cilium
|
|
kubeconfig: "{{ kubecfg_path }}"
|
|
register: flannel_cilium
|
|
|
|
- name: Assert the Cilium namespace is absent
|
|
ansible.builtin.assert:
|
|
that: flannel_cilium.resources | length == 0
|
|
success_msg: "No Cilium present with Flannel"
|
|
fail_msg: "A Cilium namespace exists alongside Flannel"
|
|
|
|
- name: Verify Calico is the active CNI
|
|
when: verify_cni == 'calico'
|
|
block:
|
|
- name: Get the Calico node DaemonSet image
|
|
kubernetes.core.k8s_info:
|
|
kind: DaemonSet
|
|
name: calico-node
|
|
namespace: calico-system
|
|
kubeconfig: "{{ kubecfg_path }}"
|
|
register: calico_node_ds
|
|
|
|
- name: Assert the Calico node image uses the expected tag
|
|
ansible.builtin.assert:
|
|
that:
|
|
- calico_node_ds.resources | length == 1
|
|
- calico_node_image | regex_search(':' ~ calico_tag)
|
|
success_msg: "Calico node image uses tag {{ calico_tag }}"
|
|
fail_msg: >-
|
|
Calico node image {{ calico_node_image }},
|
|
expected {{ calico_tag }}
|
|
vars:
|
|
calico_node_image: "{{ calico_node_ds.resources[0].spec.template.spec.containers[0].image }}"
|
|
|
|
- name: Get Calico TigeraStatus for calico and apiserver
|
|
kubernetes.core.k8s_info:
|
|
api_version: operator.tigera.io/v1
|
|
kind: TigeraStatus
|
|
name: "{{ item }}"
|
|
kubeconfig: "{{ kubecfg_path }}"
|
|
register: calico_tigerastatus
|
|
loop:
|
|
- calico
|
|
- apiserver
|
|
loop_control:
|
|
label: "Tigerastatus/{{ item }}"
|
|
|
|
- name: Assert Calico TigeraStatus reports Available
|
|
ansible.builtin.assert:
|
|
that: >-
|
|
item.resources | length == 1 and
|
|
(item.resources[0].status.conditions
|
|
| selectattr('type', 'equalto', 'Available')
|
|
| map(attribute='status') | first | default('')) == 'True'
|
|
success_msg: "Tigerastatus {{ item.resources[0].metadata.name }} is Available"
|
|
fail_msg: "Tigerastatus is not Available"
|
|
loop: "{{ calico_tigerastatus.results }}"
|
|
loop_control:
|
|
label: "Tigerastatus Available"
|
|
|
|
- name: Get any Flannel DaemonSets with Calico enabled
|
|
kubernetes.core.k8s_info:
|
|
kind: DaemonSet
|
|
namespace: kube-flannel
|
|
kubeconfig: "{{ kubecfg_path }}"
|
|
register: no_flannel_ds
|
|
|
|
- name: Assert there are no Flannel DaemonSets
|
|
ansible.builtin.assert:
|
|
that: no_flannel_ds.resources | length == 0
|
|
success_msg: "No Flannel DaemonSet present with Calico"
|
|
fail_msg: "A Flannel DaemonSet exists alongside Calico"
|
|
|
|
- name: Verify Cilium is the active CNI
|
|
when: verify_cni == 'cilium'
|
|
block:
|
|
- name: Get the Cilium agent and operator images
|
|
kubernetes.core.k8s_info:
|
|
kind: "{{ item.kind }}"
|
|
name: "{{ item.name }}"
|
|
namespace: kube-system
|
|
kubeconfig: "{{ kubecfg_path }}"
|
|
register: cilium_info
|
|
loop:
|
|
- { kind: DaemonSet, name: cilium }
|
|
- { kind: Deployment, name: cilium-operator }
|
|
loop_control:
|
|
label: "{{ item.kind }}/{{ item.name }}"
|
|
|
|
- name: Assert Cilium agent and operator use the expected image tag
|
|
ansible.builtin.assert:
|
|
that:
|
|
- cilium_agent_image | regex_search(':' ~ cilium_tag)
|
|
- cilium_operator_image | regex_search(':' ~ cilium_tag)
|
|
success_msg: "Cilium agent and operator use {{ cilium_tag }}"
|
|
fail_msg: >-
|
|
Cilium agent {{ cilium_agent_image }},
|
|
operator {{ cilium_operator_image }},
|
|
expected {{ cilium_tag }}
|
|
vars:
|
|
cilium_agent_image: >-
|
|
{{ (cilium_info.results
|
|
| selectattr('resources', 'defined')
|
|
| map(attribute='resources')
|
|
| list
|
|
| map(attribute='0')
|
|
| selectattr('kind', 'equalto', 'DaemonSet')
|
|
| list)[0].spec.template.spec.containers[0].image }}
|
|
cilium_operator_image: >-
|
|
{{ (cilium_info.results
|
|
| selectattr('resources', 'defined')
|
|
| map(attribute='resources')
|
|
| list
|
|
| map(attribute='0')
|
|
| selectattr('kind', 'equalto', 'Deployment')
|
|
| list)[0].spec.template.spec.containers[0].image }}
|
|
|
|
- name: Get Hubble relay and UI deployments when enabled
|
|
kubernetes.core.k8s_info:
|
|
kind: Deployment
|
|
name: "{{ item }}"
|
|
namespace: kube-system
|
|
kubeconfig: "{{ kubecfg_path }}"
|
|
register: hubble_info
|
|
loop:
|
|
- hubble-relay
|
|
- hubble-ui
|
|
loop_control:
|
|
label: "Deployment/{{ item }}"
|
|
when: cilium_hubble | bool
|
|
|
|
- name: Assert Hubble components are Ready when enabled
|
|
ansible.builtin.assert:
|
|
that:
|
|
- item.resources | length == 1
|
|
- item.resources[0].status.readyReplicas | default(0) >= 1
|
|
success_msg: "Hubble deployment {{ item.resources[0].metadata.name }} is Ready"
|
|
fail_msg: "Hubble deployment is not Ready"
|
|
loop: "{{ hubble_info.results }}"
|
|
loop_control:
|
|
label: "Hubble deployment"
|
|
when: cilium_hubble | bool
|
|
|
|
- name: Get any Flannel DaemonSets with Cilium enabled
|
|
kubernetes.core.k8s_info:
|
|
kind: DaemonSet
|
|
namespace: kube-flannel
|
|
kubeconfig: "{{ kubecfg_path }}"
|
|
register: no_flannel_ds_cilium
|
|
|
|
- name: Assert there are no Flannel DaemonSets
|
|
ansible.builtin.assert:
|
|
that: no_flannel_ds_cilium.resources | length == 0
|
|
success_msg: "No Flannel DaemonSet present with Cilium"
|
|
fail_msg: "A Flannel DaemonSet exists alongside Cilium"
|
|
|
|
- name: Verify MetalLB is the active load balancer
|
|
when: verify_lb == 'metallb'
|
|
block:
|
|
- name: Get the MetalLB controller and speaker images
|
|
kubernetes.core.k8s_info:
|
|
kind: "{{ item.kind }}"
|
|
name: "{{ item.name }}"
|
|
namespace: metallb-system
|
|
kubeconfig: "{{ kubecfg_path }}"
|
|
register: metallb_info
|
|
loop:
|
|
- { kind: Deployment, name: controller }
|
|
- { kind: DaemonSet, name: speaker }
|
|
loop_control:
|
|
label: "{{ item.kind }}/{{ item.name }}"
|
|
|
|
- name: Assert MetalLB controller and speaker use the expected image tags
|
|
ansible.builtin.assert:
|
|
that:
|
|
- controller_image | regex_search(metal_lb_controller_tag_version)
|
|
- speaker_image | regex_search(metal_lb_speaker_tag_version)
|
|
success_msg: >-
|
|
MetalLB controller {{ metal_lb_controller_tag_version }},
|
|
speaker {{ metal_lb_speaker_tag_version }}
|
|
fail_msg: >-
|
|
MetalLB controller {{ controller_image }},
|
|
speaker {{ speaker_image }}
|
|
vars:
|
|
controller_image: >-
|
|
{{ (metallb_info.results
|
|
| selectattr('resources', 'defined')
|
|
| map(attribute='resources')
|
|
| list
|
|
| map(attribute='0')
|
|
| selectattr('kind', 'equalto', 'Deployment')
|
|
| list)[0].spec.template.spec.containers[0].image }}
|
|
speaker_image: >-
|
|
{{ (metallb_info.results
|
|
| selectattr('resources', 'defined')
|
|
| map(attribute='resources')
|
|
| list
|
|
| map(attribute='0')
|
|
| selectattr('kind', 'equalto', 'DaemonSet')
|
|
| list)[0].spec.template.spec.containers[0].image }}
|
|
|
|
- name: Verify kube-vip is the active load balancer
|
|
when: verify_lb == 'kube-vip'
|
|
block:
|
|
- name: Get the kube-vip and cloud provider images
|
|
kubernetes.core.k8s_info:
|
|
kind: "{{ item.kind }}"
|
|
name: "{{ item.name }}"
|
|
namespace: kube-system
|
|
kubeconfig: "{{ kubecfg_path }}"
|
|
register: kubevip_info
|
|
loop:
|
|
- { kind: DaemonSet, name: kube-vip-ds }
|
|
- { kind: Deployment, name: kube-vip-cloud-provider }
|
|
loop_control:
|
|
label: "{{ item.kind }}/{{ item.name }}"
|
|
|
|
- name: Assert the kube-vip and cloud provider image tags
|
|
ansible.builtin.assert:
|
|
that:
|
|
- kubevip_image | regex_search(':' ~ kube_vip_tag_version)
|
|
- cloud_provider_image | regex_search(verify_kube_vip_cloud_provider_tag)
|
|
success_msg: >-
|
|
kube-vip {{ kube_vip_tag_version }},
|
|
cloud provider {{ verify_kube_vip_cloud_provider_tag }}
|
|
fail_msg: >-
|
|
kube-vip {{ kubevip_image }},
|
|
cloud provider {{ cloud_provider_image }}
|
|
vars:
|
|
kubevip_image: >-
|
|
{{ (kubevip_info.results
|
|
| selectattr('resources', 'defined')
|
|
| map(attribute='resources')
|
|
| list
|
|
| map(attribute='0')
|
|
| selectattr('kind', 'equalto', 'DaemonSet')
|
|
| list)[0].spec.template.spec.containers[0].image }}
|
|
cloud_provider_image: >-
|
|
{{ (kubevip_info.results
|
|
| selectattr('resources', 'defined')
|
|
| map(attribute='resources')
|
|
| list
|
|
| map(attribute='0')
|
|
| selectattr('kind', 'equalto', 'Deployment')
|
|
| list)[0].spec.template.spec.containers[0].image }}
|
|
|
|
- name: Get the MetalLB namespace with kube-vip enabled
|
|
kubernetes.core.k8s_info:
|
|
kind: Namespace
|
|
name: metallb-system
|
|
kubeconfig: "{{ kubecfg_path }}"
|
|
register: metallb_absent
|
|
|
|
- name: Assert the MetalLB namespace does not exist
|
|
ansible.builtin.assert:
|
|
that: metallb_absent.resources | length == 0
|
|
success_msg: "MetalLB is not installed with kube-vip"
|
|
fail_msg: "MetalLB namespace exists alongside kube-vip"
|