# SPDX-License-Identifier: MIT-0 --- - name: Validate rendered Kubernetes resources without a cluster hosts: localhost connection: local gather_facts: false vars: template_directory: "{{ playbook_dir }}/../templates" tasks: - name: Load the role defaults ansible.builtin.include_vars: file: "{{ playbook_dir }}/../defaults/main.yml" - name: Set required test inputs ansible.builtin.set_fact: the_button_image: registry.example.com/the-button:1.0.0 the_button_ingress_host: button.example.com - name: Render default manifests using Ansible's template engine ansible.builtin.set_fact: deployment: "{{ lookup('ansible.builtin.template', template_directory ~ '/deployment.yml.j2') | from_yaml }}" service: "{{ lookup('ansible.builtin.template', template_directory ~ '/service.yml.j2') | from_yaml }}" ingress: "{{ lookup('ansible.builtin.template', template_directory ~ '/ingress.yml.j2') | from_yaml }}" pvc: "{{ lookup('ansible.builtin.template', template_directory ~ '/pvc.yml.j2') | from_yaml }}" - name: Check TLS routing, persistent storage, and single-writer invariants ansible.builtin.assert: that: - deployment.apiVersion == 'apps/v1' - deployment.spec.replicas == 1 - deployment.spec.strategy.type == 'Recreate' - deployment.spec.selector.matchLabels == service.spec.selector - deployment.spec.template.metadata.labels == service.spec.selector - deployment.spec.template.spec.volumes[0].persistentVolumeClaim.claimName == pvc.metadata.name - deployment.spec.template.spec.containers[0].volumeMounts[0].mountPath == '/data' - deployment.spec.template.spec.containers[0].image == the_button_image - deployment.spec.template.spec.containers[0].ports[0].containerPort == 8000 - service.spec.type == 'ClusterIP' - service.spec.ports[0].port == 80 - service.spec.ports[0].targetPort == 'http' - ingress.apiVersion == 'networking.k8s.io/v1' - ingress.metadata.annotations['cert-manager.io/cluster-issuer'] == 'ca-issuer' - ingress.spec.tls[0].hosts == [the_button_ingress_host] - ingress.spec.tls[0].secretName == 'the-button-tls' - ingress.spec.rules[0].host == the_button_ingress_host - ingress.spec.rules[0].http.paths[0].backend.service.name == service.metadata.name - ingress.spec.rules[0].http.paths[0].backend.service.port.name == 'http' - ingress.spec.rules[0].http.paths[0].pathType == 'Prefix' - "'ingressClassName' not in ingress.spec" - "'storageClassName' not in pvc.spec" - pvc.spec.accessModes == ['ReadWriteOnce'] - pvc.spec.resources.requests.storage == '1Gi' - name: Check container hardening and health probes ansible.builtin.assert: that: - not deployment.spec.template.spec.automountServiceAccountToken - deployment.spec.template.spec.securityContext.runAsNonRoot - deployment.spec.template.spec.securityContext.runAsUser == 10001 - deployment.spec.template.spec.securityContext.fsGroup == 10001 - deployment.spec.template.spec.securityContext.seccompProfile.type == 'RuntimeDefault' - not deployment.spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation - deployment.spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem - deployment.spec.template.spec.containers[0].securityContext.capabilities.drop == ['ALL'] - deployment.spec.template.spec.containers[0].startupProbe.httpGet.path == '/api/state' - deployment.spec.template.spec.containers[0].readinessProbe.httpGet.port == 'http' - deployment.spec.template.spec.containers[0].livenessProbe.httpGet.port == 'http' - name: Configure custom deployment settings ansible.builtin.set_fact: the_button_name: custom-button the_button_namespace: games the_button_ingress_class_name: nginx the_button_ingress_annotations: nginx.ingress.kubernetes.io/ssl-redirect: "true" cert-manager.io/cluster-issuer: custom-ca-issuer the_button_existing_claim: restored-game-data the_button_storage_class: fast the_button_image_pull_secrets: - private-registry the_button_duration_seconds: 60 the_button_node_selector: kubernetes.io/os: linux the_button_tolerations: - key: games operator: Exists effect: NoSchedule - name: Render custom manifests ansible.builtin.set_fact: deployment: "{{ lookup('ansible.builtin.template', template_directory ~ '/deployment.yml.j2') | from_yaml }}" ingress: "{{ lookup('ansible.builtin.template', template_directory ~ '/ingress.yml.j2') | from_yaml }}" pvc: "{{ lookup('ansible.builtin.template', template_directory ~ '/pvc.yml.j2') | from_yaml }}" - name: Check configurable settings and custom annotations ansible.builtin.assert: that: - deployment.metadata.name == 'custom-button' - deployment.metadata.namespace == 'games' - deployment.spec.template.spec.volumes[0].persistentVolumeClaim.claimName == 'restored-game-data' - deployment.spec.template.spec.imagePullSecrets == [{'name':'private-registry'}] - deployment.spec.template.spec.nodeSelector['kubernetes.io/os'] == 'linux' - deployment.spec.template.spec.tolerations[0].key == 'games' - deployment.spec.template.spec.containers[0].env[2].value == '60' - ingress.spec.ingressClassName == 'nginx' - ingress.metadata.annotations['nginx.ingress.kubernetes.io/ssl-redirect'] == 'true' - ingress.metadata.annotations['cert-manager.io/cluster-issuer'] == 'custom-ca-issuer' - ingress.spec.tls[0].secretName == 'custom-button-tls' - pvc.spec.storageClassName == 'fast' - name: Configure a namespace-scoped issuer through annotations ansible.builtin.set_fact: the_button_ingress_annotations: cert-manager.io/issuer: namespace-issuer - name: Render ingress with a namespace-scoped issuer ansible.builtin.set_fact: ingress: "{{ lookup('ansible.builtin.template', template_directory ~ '/ingress.yml.j2') | from_yaml }}" - name: Check issuer annotations are rendered without injected defaults ansible.builtin.assert: that: - ingress.metadata.annotations == the_button_ingress_annotations - "'cert-manager.io/cluster-issuer' not in ingress.metadata.annotations" - name: Configure an empty annotations map ansible.builtin.set_fact: the_button_ingress_annotations: {} - name: Render ingress without annotations ansible.builtin.set_fact: ingress: "{{ lookup('ansible.builtin.template', template_directory ~ '/ingress.yml.j2') | from_yaml }}" - name: Check annotations can be cleared entirely ansible.builtin.assert: that: - ingress.metadata.annotations == {} - name: Disable dynamic provisioning ansible.builtin.set_fact: the_button_storage_class: "" - name: Render a claim with dynamic provisioning disabled ansible.builtin.set_fact: static_pvc: "{{ lookup('ansible.builtin.template', template_directory ~ '/pvc.yml.j2') | from_yaml }}" - name: Check explicit empty StorageClass is preserved ansible.builtin.assert: that: - static_pvc.spec.storageClassName == ''