--- # Offline tests: no Kubernetes credentials or API calls. - name: Validate role configuration and rendered Kubernetes resources hosts: localhost connection: local gather_facts: false vars_files: - ../defaults/main.yml tasks: - name: Test rendering variants ansible.builtin.include_tasks: render_case.yml vars: wikideck_image: "{{ render_case.image | default('registry.example.com/wikideck:1.0.0') }}" wikideck_name: "{{ render_case.name | default('wikideck') }}" wikideck_namespace: "{{ render_case.namespace | default('wikideck') }}" wikideck_peers: "{{ render_case.peers | default([]) }}" wikideck_allow_private_peers: "{{ render_case.allow_private | default(false) }}" wikideck_startup_probe_failure_threshold: "{{ render_case.startup_threshold | default(180) }}" wikideck_ingress_enabled: "{{ render_case.ingress | default(false) }}" wikideck_ingress_host: wikideck.example.com wikideck_public_origin: "{{ 'https://wikideck.example.com' if render_case.ingress | default(false) else '' }}" wikideck_ingress_class_name: "{{ render_case.ingress_class | default('') }}" wikideck_ingress_annotations: "{{ render_case.annotations | default({}) }}" wikideck_storage_class: "{{ render_case.storage_class | default(none) }}" wikideck_existing_claim: "{{ render_case.existing_claim | default('') }}" wikideck_image_pull_secrets: "{{ render_case.pull_secrets | default([]) }}" wikideck_network_policy_ingress_from: "{{ render_case.ingress_from | default([{'podSelector': {}}]) }}" wikideck_network_policy_dns_to: "{{ render_case.dns_to | default([{'namespaceSelector': {}}]) }}" wikideck_network_policy_https_to: "{{ render_case.https_to | default([{'ipBlock': {'cidr': '0.0.0.0/0'}}]) }}" wikideck_network_policy_https_ports: "{{ render_case.https_ports | default([443]) }}" wikideck_network_policy_peer_egress: "{{ render_case.peer_egress | default([]) }}" loop: - label: isolated validator defaults - label: TLS ingress and existing storage peers: [https://peer-a.example.org, https://peer-b.example.org] ingress: true ingress_class: nginx annotations: cert-manager.io/cluster-issuer: letsencrypt-prod existing_claim: existing-chain storage_class: fast-encrypted pull_secrets: [registry-credentials] name: custom-deck namespace: games - label: isolated validator and no storage class storage_class: "" - label: default-deny all traffic ingress_from: [] dns_to: [] https_to: [] - label: public peers with custom HTTPS port and long startup peers: [https://peer.example.org:8443] https_ports: [443, 8443] startup_threshold: 360 - label: private HTTP testnet with restricted egress peers: [http://other-node.games.svc.cluster.local:80] allow_private: true https_to: [] peer_egress: - to: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: games podSelector: matchLabels: app.kubernetes.io/instance: other-node ports: - protocol: TCP port: 3001 - label: public peer with all egress explicitly denied peers: [https://peer.example.org] dns_to: [] https_to: [] - label: digest-pinned image image: registry.example.com/wikideck@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa loop_control: loop_var: render_case label: "{{ render_case.label }}" - name: Test invalid settings are rejected ansible.builtin.include_tasks: invalid_case.yml loop: - label: missing image image: "" - label: untagged image image: registry.example.com/wikideck - label: registry port is not an image tag image: registry.example.com:5000/wikideck - label: malformed image digest image: registry.example.com/wikideck@sha256:abcd - label: invalid resource name name: Invalid_Name - label: unsafe multi-writer storage access_modes: [ReadWriteMany] - label: missing ingress origin ingress: true - label: mismatched ingress origin ingress: true origin: https://another.example.com - label: insecure public origin origin: http://wikideck.example.com - label: origin with path origin: https://wikideck.example.com/path - label: invalid ingress hostname ingress: true host: invalid_host origin: https://wikideck.example.com - label: missing resource limits resources: requests: {cpu: 100m, memory: 128Mi} - label: ambiguous boolean allow_private: "false" - label: wrong list type pull_secrets: not-a-list - label: empty peer allows all traffic ingress_from: [{}] - label: too many peers peers: [https://a.example.org, https://b.example.org, https://c.example.org, https://d.example.org, https://e.example.org, https://f.example.org, https://g.example.org, https://h.example.org, https://i.example.org, https://j.example.org, https://k.example.org, https://l.example.org, https://m.example.org, https://n.example.org, https://o.example.org, https://p.example.org, https://q.example.org] - label: peers must be a list peers: https://peer.example.org - label: HTTP peers need explicit opt-in peers: [http://peer.example.org] - label: credentials in peer URL peers: [https://user:password@peer.example.org] - label: path in peer URL peers: [https://peer.example.org/p2p] - label: query in peer URL peers: ['https://peer.example.org?x=1'] - label: comma can inject a second peer peers: ['https://peer.example.org,https://another.example.org'] - label: zero startup threshold startup_threshold: 0 - label: invalid HTTPS egress port https_ports: [65536] - label: egress rule without destinations peer_egress: - ports: [{protocol: TCP, port: 3001}] - label: egress rule without ports peer_egress: - to: [{podSelector: {}}] - label: egress rule with an empty peer peer_egress: - to: [{}] ports: [{protocol: TCP, port: 3001}] - label: non-TCP peer egress peer_egress: - to: [{podSelector: {}}] ports: [{protocol: UDP, port: 3001}] loop_control: loop_var: invalid_case label: "{{ invalid_case.label }}"