151 lines
7.6 KiB
YAML
151 lines
7.6 KiB
YAML
# 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 == ''
|