From 64e133d1ade58b2fec1dfe342402036044a675ec Mon Sep 17 00:00:00 2001 From: Nishan Date: Mon, 21 Apr 2025 12:20:01 +0530 Subject: [PATCH] ADD: CKA ADM Lab --- facilitator/assets/exams/cka/001/config.json | 2 +- facilitator/assets/exams/cka/002/answers.md | 1088 +++++++++++++++++ facilitator/assets/exams/cka/002/answers.sh | 999 +++++++++++++++ .../assets/exams/cka/002/assessment.json | 597 +++++++++ facilitator/assets/exams/cka/002/config.json | 10 + .../exams/cka/002/scripts/setup/q10_setup.sh | 10 + .../exams/cka/002/scripts/setup/q11_setup.sh | 16 + .../exams/cka/002/scripts/setup/q12_setup.sh | 18 + .../exams/cka/002/scripts/setup/q13_setup.sh | 14 + .../exams/cka/002/scripts/setup/q14_setup.sh | 11 + .../exams/cka/002/scripts/setup/q15_setup.sh | 16 + .../exams/cka/002/scripts/setup/q16_setup.sh | 7 + .../exams/cka/002/scripts/setup/q17_setup.sh | 16 + .../exams/cka/002/scripts/setup/q18_setup.sh | 13 + .../exams/cka/002/scripts/setup/q19_setup.sh | 29 + .../exams/cka/002/scripts/setup/q1_setup.sh | 17 + .../exams/cka/002/scripts/setup/q20_setup.sh | 41 + .../exams/cka/002/scripts/setup/q2_setup.sh | 18 + .../exams/cka/002/scripts/setup/q3_setup.sh | 13 + .../exams/cka/002/scripts/setup/q4_setup.sh | 14 + .../exams/cka/002/scripts/setup/q5_setup.sh | 10 + .../exams/cka/002/scripts/setup/q6_setup.sh | 37 + .../exams/cka/002/scripts/setup/q7_setup.sh | 13 + .../exams/cka/002/scripts/setup/q8_setup.sh | 17 + .../exams/cka/002/scripts/setup/q9_setup.sh | 13 + .../validation/q10_s1_validate_deployment.sh | 31 + .../scripts/validation/q10_s2_validate_dns.sh | 30 + .../validation/q10_s3_validate_results.sh | 24 + .../validation/q11_s1_validate_repo.sh | 24 + .../validation/q11_s2_validate_release.sh | 32 + .../validation/q11_s3_validate_deployment.sh | 32 + .../validation/q12_s1_validate_files.sh | 52 + .../validation/q12_s2_validate_resources.sh | 39 + .../validation/q12_s3_validate_configmap.sh | 38 + .../validation/q13_s1_validate_gateway.sh | 32 + .../validation/q13_s2_validate_httproute.sh | 46 + .../validation/q13_s3_validate_backends.sh | 52 + .../validation/q14_s1_validate_limitrange.sh | 50 + .../validation/q14_s2_validate_quota.sh | 39 + .../validation/q14_s3_validate_deployment.sh | 55 + .../validation/q15_s1_validate_deployment.sh | 51 + .../scripts/validation/q15_s2_validate_hpa.sh | 39 + .../validation/q15_s3_validate_pods.sh | 44 + .../scripts/validation/q16_s1_validate_sa.sh | 31 + .../validation/q16_s2_validate_rbac.sh | 54 + .../scripts/validation/q16_s3_validate_pod.sh | 55 + .../validation/q17_s1_validate_deployments.sh | 71 ++ .../validation/q17_s2_validate_policies.sh | 33 + .../validation/q17_s3_validate_enforcement.sh | 54 + .../validation/q18_s1_validate_initial.sh | 53 + .../validation/q18_s2_validate_update.sh | 56 + .../validation/q18_s3_validate_rollback.sh | 50 + .../validation/q19_s1_validate_priority.sh | 40 + .../validation/q19_s2_validate_pods.sh | 40 + .../q19_s3_validate_antiaffinity.sh | 42 + .../scripts/validation/q1_s1_validate_pvc.sh | 32 + .../scripts/validation/q1_s2_validate_pod.sh | 25 + .../validation/q1_s3_validate_mount.sh | 19 + .../validation/q20_s1_validate_port.sh | 28 + .../validation/q20_s2_validate_memory.sh | 39 + .../validation/q20_s3_validate_probe.sh | 37 + .../validation/q20_s4_validate_pods.sh | 61 + .../scripts/validation/q2_s1_validate_sc.sh | 25 + .../validation/q2_s2_validate_default.sh | 12 + .../q2_s3_validate_no_other_default.sh | 20 + .../scripts/validation/q3_s1_validate_pv.sh | 39 + .../scripts/validation/q3_s2_validate_pvc.sh | 39 + .../scripts/validation/q3_s3_validate_pod.sh | 52 + .../validation/q4_s1_validate_deployment.sh | 51 + .../scripts/validation/q4_s2_validate_hpa.sh | 39 + .../validation/q4_s3_validate_resources.sh | 46 + .../validation/q5_s1_validate_node_label.sh | 18 + .../validation/q5_s2_validate_affinity.sh | 41 + .../q5_s3_validate_pod_placement.sh | 24 + .../scripts/validation/q6_s1_validate_psp.sh | 27 + .../scripts/validation/q6_s2_validate_pod.sh | 39 + .../validation/q7_s1_validate_taint.sh | 32 + .../q7_s2_validate_toleration_deploy.sh | 51 + .../q7_s3_validate_normal_deploy.sh | 49 + .../validation/q8_s1_validate_statefulset.sh | 52 + .../validation/q8_s2_validate_service.sh | 40 + .../validation/q8_s3_validate_storage.sh | 37 + .../q9_s1_validate_deployment_svc.sh | 52 + .../q9_s2_validate_dns_resolution.sh | 38 + .../validation/q9_s3_validate_dns_config.sh | 32 + facilitator/assets/exams/ckad/001/answers.md | 8 +- .../assets/exams/ckad/001/assessment.json | 2 +- facilitator/assets/exams/labs.json | 10 + facilitator/src/services/examService.js | 4 +- 89 files changed, 5439 insertions(+), 9 deletions(-) create mode 100644 facilitator/assets/exams/cka/002/answers.md create mode 100644 facilitator/assets/exams/cka/002/answers.sh create mode 100644 facilitator/assets/exams/cka/002/assessment.json create mode 100644 facilitator/assets/exams/cka/002/config.json create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q10_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q11_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q12_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q13_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q14_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q15_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q16_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q17_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q18_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q19_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q1_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q20_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q2_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q3_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q4_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q5_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q6_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q7_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q8_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/setup/q9_setup.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q10_s1_validate_deployment.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q10_s2_validate_dns.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q10_s3_validate_results.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q11_s1_validate_repo.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q11_s2_validate_release.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q11_s3_validate_deployment.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q12_s1_validate_files.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q12_s2_validate_resources.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q12_s3_validate_configmap.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q13_s1_validate_gateway.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q13_s2_validate_httproute.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q13_s3_validate_backends.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q14_s1_validate_limitrange.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q14_s2_validate_quota.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q14_s3_validate_deployment.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q15_s1_validate_deployment.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q15_s2_validate_hpa.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q15_s3_validate_pods.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q16_s1_validate_sa.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q16_s2_validate_rbac.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q16_s3_validate_pod.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q17_s1_validate_deployments.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q17_s2_validate_policies.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q17_s3_validate_enforcement.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q18_s1_validate_initial.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q18_s2_validate_update.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q18_s3_validate_rollback.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q19_s1_validate_priority.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q19_s2_validate_pods.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q19_s3_validate_antiaffinity.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q1_s1_validate_pvc.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q1_s2_validate_pod.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q1_s3_validate_mount.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q20_s1_validate_port.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q20_s2_validate_memory.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q20_s3_validate_probe.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q20_s4_validate_pods.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q2_s1_validate_sc.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q2_s2_validate_default.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q2_s3_validate_no_other_default.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q3_s1_validate_pv.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q3_s2_validate_pvc.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q3_s3_validate_pod.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q4_s1_validate_deployment.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q4_s2_validate_hpa.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q4_s3_validate_resources.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q5_s1_validate_node_label.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q5_s2_validate_affinity.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q5_s3_validate_pod_placement.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q6_s1_validate_psp.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q6_s2_validate_pod.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q7_s1_validate_taint.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q7_s2_validate_toleration_deploy.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q7_s3_validate_normal_deploy.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q8_s1_validate_statefulset.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q8_s2_validate_service.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q8_s3_validate_storage.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q9_s1_validate_deployment_svc.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q9_s2_validate_dns_resolution.sh create mode 100644 facilitator/assets/exams/cka/002/scripts/validation/q9_s3_validate_dns_config.sh diff --git a/facilitator/assets/exams/cka/001/config.json b/facilitator/assets/exams/cka/001/config.json index b596e83..3a7612b 100644 --- a/facilitator/assets/exams/cka/001/config.json +++ b/facilitator/assets/exams/cka/001/config.json @@ -1,6 +1,6 @@ { "lab": "cka-001", - "workerNodes": 2, + "workerNodes": 1, "answers": "assets/exams/cka/001/answers.md", "questions": "assessment.json", "totalMarks": 100, diff --git a/facilitator/assets/exams/cka/002/answers.md b/facilitator/assets/exams/cka/002/answers.md new file mode 100644 index 0000000..3c99e2f --- /dev/null +++ b/facilitator/assets/exams/cka/002/answers.md @@ -0,0 +1,1088 @@ +# CKA Exam Solutions + +This document contains detailed solutions for the CKA exam questions. Each solution includes step-by-step instructions and explanations. + +## Question 1: Dynamic PVC and Pod + +### Solution +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: data-pvc + namespace: storage-task +spec: + storageClassName: standard + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi +--- +apiVersion: v1 +kind: Pod +metadata: + name: data-pod + namespace: storage-task +spec: + containers: + - name: nginx + image: nginx + volumeMounts: + - name: data + mountPath: /usr/share/nginx/html + volumes: + - name: data + persistentVolumeClaim: + claimName: data-pvc +``` + +## Question 2: Storage Class Configuration + +### Solution +```yaml +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: fast-local + annotations: + storageclass.kubernetes.io/is-default-class: "true" +provisioner: rancher.io/local-path +volumeBindingMode: WaitForFirstConsumer +``` + +Commands to remove default from other StorageClass: +```bash +kubectl patch storageclass default-test -p '{"metadata": {"annotations": {"storageclass.kubernetes.io/is-default-class": "false"}}}' +kubectl patch storageclass local-path -p '{"metadata": {"annotations": {"storageclass.kubernetes.io/is-default-class": "false"}}}' +``` + +## Question 3: Manual Storage Configuration + +### Solution +```yaml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: manual-pv +spec: + capacity: + storage: 1Gi + accessModes: + - ReadWriteOnce + hostPath: + path: /mnt/data + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - k3d-cluster-agent-0 +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: manual-pvc + namespace: manual-storage +spec: + storageClassName: "" + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: v1 +kind: Pod +metadata: + name: manual-pod + namespace: manual-storage +spec: + containers: + - name: busybox + image: busybox + command: ["sleep", "3600"] + volumeMounts: + - name: data + mountPath: /data + volumes: + - name: data + persistentVolumeClaim: + claimName: manual-pvc +``` + +## Question 4: Deployment with HPA + +### Solution +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: scaling-app + namespace: scaling +spec: + replicas: 2 + selector: + matchLabels: + app: scaling-app + template: + metadata: + labels: + app: scaling-app + spec: + containers: + - name: nginx + image: nginx + resources: + requests: + cpu: 200m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: scaling-app + namespace: scaling +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: scaling-app + minReplicas: 2 + maxReplicas: 5 + targetCPUUtilizationPercentage: 70 +``` + +## Question 5: Node Affinity Configuration + +### Solution +```bash +# Label the node +kubectl label node k3d-cluster-agent-1 disk=ssd +``` + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app-scheduling + namespace: scheduling +spec: + replicas: 3 + selector: + matchLabels: + app: app-scheduling + template: + metadata: + labels: + app: app-scheduling + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: disk + operator: In + values: + - ssd + containers: + - name: nginx + image: nginx +``` + +## Question 6: Pod Security Policy + +### Solution +```bash +kubectl label namespace security pod-security.kubernetes.io/enforce=restricted pod-security.kubernetes.io/enforce-version=latest +``` + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: secure-pod + namespace: security +spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault + containers: + - name: nginx + image: nginx + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 + capabilities: + drop: + - ALL + volumes: + - name: html + emptyDir: {} +``` + +## Question 7: Node Taints and Tolerations + +### Solution +```bash +kubectl taint node k3d-cluster-agent-1 special-workload=true:NoSchedule +``` + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: toleration-deploy + namespace: scheduling +spec: + replicas: 2 + selector: + matchLabels: + app: toleration-deploy + template: + metadata: + labels: + app: toleration-deploy + spec: + tolerations: + - key: "special-workload" + operator: "Equal" + value: "true" + effect: "NoSchedule" + containers: + - name: nginx + image: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: normal-deploy + namespace: scheduling +spec: + replicas: 2 + selector: + matchLabels: + app: normal-deploy + template: + metadata: + labels: + app: normal-deploy + spec: + containers: + - name: nginx + image: nginx +``` + +## Question 8: StatefulSet and Headless Service + +### Solution +``bash +kubectl create namespace stateful +`` + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: web-svc + namespace: stateful +spec: + clusterIP: None + selector: + app: web + ports: + - port: 80 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: web + namespace: stateful +spec: + serviceName: web-svc + replicas: 3 + selector: + matchLabels: + app: web + template: + metadata: + labels: + app: web + spec: + containers: + - name: nginx + image: nginx + volumeMounts: + - name: www + mountPath: /usr/share/nginx/html + volumeClaimTemplates: + - metadata: + name: www + spec: + accessModes: ["ReadWriteOnce"] + storageClassName: cold + resources: + requests: + storage: 1Gi +``` + +## Question 9: DNS Configuration and Debugging + +### Solution +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: web-app + namespace: dns-debug +spec: + replicas: 3 + selector: + matchLabels: + app: web-app + template: + metadata: + labels: + app: web-app + spec: + containers: + - name: nginx + image: nginx +--- +apiVersion: v1 +kind: Service +metadata: + name: web-svc + namespace: dns-debug +spec: + selector: + app: web-app + ports: + - port: 80 +--- +apiVersion: v1 +kind: Pod +metadata: + name: dns-test + namespace: dns-debug +spec: + containers: + - name: busybox + image: busybox + command: + - sh + - -c + - "wget -qO- http://web-svc && wget -qO- http://web-svc.dns-debug.svc.cluster.local && sleep 36000" + dnsConfig: + searches: + - dns-debug.svc.cluster.local + - svc.cluster.local + - cluster.local +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: dns-config + namespace: dns-debug +data: + search-domains: | + search dns-debug.svc.cluster.local svc.cluster.local cluster.local +``` + +## Question 10: Basic DNS Service Discovery + +### Solution +```yaml +# Create the deployment +apiVersion: apps/v1 +kind: Deployment +metadata: + name: dns-app + namespace: dns-config +spec: + replicas: 2 + selector: + matchLabels: + app: dns-app + template: + metadata: + labels: + app: dns-app + spec: + containers: + - name: nginx + image: nginx + ports: + - containerPort: 80 +--- +# Create the service +apiVersion: v1 +kind: Service +metadata: + name: dns-svc + namespace: dns-config +spec: + selector: + app: dns-app + ports: + - port: 80 + targetPort: 80 +--- +# Create the DNS tester pod +apiVersion: v1 +kind: Pod +metadata: + name: dns-tester + namespace: dns-config +spec: + containers: + - name: dns-tester + image: infoblox/dnstools + command: + - sh + - -c + - | + nslookup dns-svc > /tmp/dns-test.txt + nslookup dns-svc.dns-config.svc.cluster.local >> /tmp/dns-test.txt + sleep 3600 +``` + +To verify the setup: +```bash +# Check if service is resolvable +kubectl exec -n dns-config dns-tester -- cat /tmp/dns-test.txt + +# Verify deployment is running +kubectl get deployment -n dns-config dns-app + +# Check service endpoints +kubectl get endpoints -n dns-config dns-svc +``` + +## Question 11: Helm Chart Deployment + +### Solution +```bash +# Add bitnami repo +helm repo add bitnami https://charts.bitnami.com/bitnami +helm repo update + +# Install nginx chart +helm install web-release bitnami/nginx \ + --namespace helm-test \ + --set service.type=NodePort \ + --set replicaCount=2 +``` + +## Question 12: Kustomize Configuration + +### Solution + +First, create the directory structure: +```bash +mkdir -p /tmp/exam/kustomize/base +mkdir -p /tmp/exam/kustomize/overlays/production +``` + +Create base files: + +```yaml +# /tmp/exam/kustomize/base/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx +spec: + replicas: 2 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx + volumeMounts: + - name: nginx-index + mountPath: /usr/share/nginx/html/ + volumes: + - name: nginx-index + configMap: + name: nginx-config + +# /tmp/exam/kustomize/base/kustomization.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- deployment.yaml + +# /tmp/exam/kustomize/overlays/production/kustomization.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: kustomize +bases: +- ../../base +patches: +- patch: | + - op: replace + path: /spec/replicas + value: 3 + target: + kind: Deployment + name: nginx +commonLabels: + environment: production +configMapGenerator: +- name: nginx-config + literals: + - index.html=Welcome to Production +``` + +Apply the configuration: +```bash +kubectl create namespace kustomize +kubectl apply -k /tmp/exam/kustomize/overlays/production/ +``` + +Verify the deployment: +```bash +kubectl get deployments -n kustomize +kubectl get configmaps -n kustomize +kubectl get pods -n kustomize +``` + +## Question 13: Gateway API Configuration + +### Solution +```yaml +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: main-gateway + namespace: gateway +spec: + gatewayClassName: standard + listeners: + - name: http + port: 80 + protocol: HTTP +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: app-routes + namespace: gateway +spec: + parentRefs: + - name: main-gateway + rules: + - matches: + - path: + value: /app1 + backendRefs: + - name: app1-svc + port: 8080 + - matches: + - path: + value: /app2 + backendRefs: + - name: app2-svc + port: 8080 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app1 + namespace: gateway +spec: + replicas: 1 + selector: + matchLabels: + app: app1 + template: + metadata: + labels: + app: app1 + spec: + containers: + - name: nginx + image: nginx +--- +apiVersion: v1 +kind: Service +metadata: + name: app1-svc + namespace: gateway +spec: + selector: + app: app1 + ports: + - port: 8080 + targetPort: 80 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app2 + namespace: gateway +spec: + replicas: 1 + selector: + matchLabels: + app: app2 + template: + metadata: + labels: + app: app2 + spec: + containers: + - name: nginx + image: nginx +--- +apiVersion: v1 +kind: Service +metadata: + name: app2-svc + namespace: gateway +spec: + selector: + app: app2 + ports: + - port: 8080 + targetPort: 80 +``` + +## Question 14: Resource Limits and Quotas + +### Solution +```yaml +apiVersion: v1 +kind: LimitRange +metadata: + name: resource-limits + namespace: limits +spec: + limits: + - type: Container + default: + cpu: 200m + memory: 256Mi + defaultRequest: + cpu: 100m + memory: 128Mi + max: + cpu: 500m + memory: 512Mi +--- +apiVersion: v1 +kind: ResourceQuota +metadata: + name: compute-quota + namespace: limits +spec: + hard: + cpu: "2" + memory: 2Gi + pods: "5" +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-limits + namespace: limits +spec: + replicas: 2 + selector: + matchLabels: + app: test-limits + template: + metadata: + labels: + app: test-limits + spec: + containers: + - name: nginx + image: nginx +``` + +## Question 15: Horizontal Pod Autoscaling + +### Solution +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: resource-consumer + namespace: monitoring +spec: + replicas: 3 + selector: + matchLabels: + app: resource-consumer + template: + metadata: + labels: + app: resource-consumer + spec: + containers: + - name: resource-consumer + image: gcr.io/kubernetes-e2e-test-images/resource-consumer:1.5 + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 200m + memory: 256Mi +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: resource-consumer + namespace: monitoring +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: resource-consumer + minReplicas: 3 + maxReplicas: 6 + targetCPUUtilizationPercentage: 50 +``` + +## Question 16: RBAC Configuration + +### Solution +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: app-admin + namespace: cluster-admin +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: app-admin + namespace: cluster-admin +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["list", "get", "watch"] +- apiGroups: ["apps"] + resources: ["deployments"] + verbs: ["list", "get", "watch", "update"] +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["create", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: app-admin + namespace: cluster-admin +subjects: +- kind: ServiceAccount + name: app-admin + namespace: cluster-admin +roleRef: + kind: Role + name: app-admin + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: v1 +kind: Pod +metadata: + name: admin-pod + namespace: cluster-admin +spec: + serviceAccountName: app-admin + containers: + - name: kubectl + image: bitnami/kubectl:latest + command: ["sleep", "3600"] + volumeMounts: + - name: token + mountPath: /var/run/secrets/kubernetes.io/serviceaccount + volumes: + - name: token + projected: + sources: + - serviceAccountToken: + expirationSeconds: 3600 + audience: kubernetes.default.svc +``` + +## Question 17: Network Policies + +### Solution +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: web + namespace: network +spec: + replicas: 1 + selector: + matchLabels: + app: web + template: + metadata: + labels: + app: web + spec: + containers: + - name: nginx + image: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: api + namespace: network +spec: + replicas: 1 + selector: + matchLabels: + app: api + template: + metadata: + labels: + app: api + spec: + containers: + - name: nginx + image: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: db + namespace: network +spec: + replicas: 1 + selector: + matchLabels: + app: db + template: + metadata: + labels: + app: db + spec: + containers: + - name: postgres + image: postgres + env: + - name: POSTGRES_HOST_AUTH_METHOD + value: trust +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: web-policy + namespace: network +spec: + podSelector: + matchLabels: + app: web + policyTypes: + - Egress + egress: + - to: + - podSelector: + matchLabels: + app: api +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: api-policy + namespace: network +spec: + podSelector: + matchLabels: + app: api + policyTypes: + - Egress + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app: web + egress: + - to: + - podSelector: + matchLabels: + app: db +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: db-policy + namespace: network +spec: + podSelector: + matchLabels: + app: db + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app: api +``` + +## Question 18: Rolling Updates + +### Solution +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app-v1 + namespace: upgrade +spec: + replicas: 4 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + selector: + matchLabels: + app: app-v1 + template: + metadata: + labels: + app: app-v1 + spec: + containers: + - name: nginx + image: nginx:1.19 +``` + +```bash +# Perform update +kubectl set image deployment/app-v1 nginx=nginx:1.20 -n upgrade --record + +# Save rollout history +kubectl rollout history deployment app-v1 -n upgrade > /tmp/exam/rollout-history.txt + +# Rollback +kubectl rollout undo deployment/app-v1 -n upgrade +``` + +## Question 19: Pod Priority and Anti-affinity + +### Solution +```yaml +apiVersion: scheduling.k8s.io/v1 +kind: PriorityClass +metadata: + name: high-priority +value: 1000 +--- +apiVersion: scheduling.k8s.io/v1 +kind: PriorityClass +metadata: + name: low-priority +value: 100 +--- +apiVersion: v1 +kind: Pod +metadata: + name: high-priority + namespace: scheduling + labels: + priority: high # ✅ Add this +spec: + priorityClassName: high-priority + containers: + - name: nginx + image: nginx + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: priority + operator: In + values: + - high + - low + topologyKey: kubernetes.io/hostname +--- +apiVersion: v1 +kind: Pod +metadata: + name: low-priority + namespace: scheduling + labels: + priority: low # ✅ Add this +spec: + priorityClassName: low-priority + containers: + - name: nginx + image: nginx + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: priority + operator: In + values: + - high + - low + topologyKey: kubernetes.io/hostname +``` + +## Question 20: Troubleshooting Application + +### Solution +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: failing-app + namespace: troubleshoot +spec: + replicas: 3 + selector: + matchLabels: + app: failing-app + template: + metadata: + labels: + app: failing-app + spec: + containers: + - name: nginx + image: nginx:1.25 + ports: + - containerPort: 80 # Fixed from 8080 + resources: + limits: + memory: 256Mi # Fixed from 64Mi + livenessProbe: + httpGet: + path: / + port: 80 # Fixed from 8080 + initialDelaySeconds: 15 + periodSeconds: 10 +``` \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/answers.sh b/facilitator/assets/exams/cka/002/answers.sh new file mode 100644 index 0000000..c25eeaa --- /dev/null +++ b/facilitator/assets/exams/cka/002/answers.sh @@ -0,0 +1,999 @@ +#!/bin/bash + +# answers.md convetred to answers.sh for quick testing +echo "Implementing Question 1: Dynamic PVC and Pod" +cat <<'EOF' | kubectl apply -f - +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: data-pvc + namespace: storage-task +spec: + storageClassName: standard + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 2Gi +--- +apiVersion: v1 +kind: Pod +metadata: + name: data-pod + namespace: storage-task +spec: + containers: + - name: nginx + image: nginx + volumeMounts: + - name: data + mountPath: /usr/share/nginx/html + volumes: + - name: data + persistentVolumeClaim: + claimName: data-pvc +EOF + +echo "Implementing Question 2: Storage Class Configuration" +cat <<'EOF' | kubectl apply -f - +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: fast-local + annotations: + storageclass.kubernetes.io/is-default-class: "true" +provisioner: rancher.io/local-path +volumeBindingMode: WaitForFirstConsumer +EOF + +kubectl patch storageclass default-test -p '{"metadata": {"annotations": {"storageclass.kubernetes.io/is-default-class": "false"}}}' +kubectl patch storageclass local-path -p '{"metadata": {"annotations": {"storageclass.kubernetes.io/is-default-class": "false"}}}' + +echo "Implementing Question 3: Manual Storage Configuration" +cat <<'EOF' | kubectl apply -f - +apiVersion: v1 +kind: PersistentVolume +metadata: + name: manual-pv +spec: + capacity: + storage: 1Gi + accessModes: + - ReadWriteOnce + hostPath: + path: /mnt/data + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - k3d-cluster-agent-0 +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: manual-pvc + namespace: manual-storage +spec: + storageClassName: "" + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: v1 +kind: Pod +metadata: + name: manual-pod + namespace: manual-storage +spec: + containers: + - name: busybox + image: busybox + command: ["sleep", "3600"] + volumeMounts: + - name: data + mountPath: /data + volumes: + - name: data + persistentVolumeClaim: + claimName: manual-pvc +EOF + +echo "Implementing Question 4: Deployment with HPA" +cat <<'EOF' | kubectl apply -f - +apiVersion: apps/v1 +kind: Deployment +metadata: + name: scaling-app + namespace: scaling +spec: + replicas: 2 + selector: + matchLabels: + app: scaling-app + template: + metadata: + labels: + app: scaling-app + spec: + containers: + - name: nginx + image: nginx + resources: + requests: + cpu: 200m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: scaling-app + namespace: scaling +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: scaling-app + minReplicas: 2 + maxReplicas: 5 + targetCPUUtilizationPercentage: 70 +EOF + +echo "Implementing Question 5: Node Affinity Configuration" +kubectl label node k3d-cluster-agent-1 disk=ssd + +cat <<'EOF' | kubectl apply -f - +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app-scheduling + namespace: scheduling +spec: + replicas: 3 + selector: + matchLabels: + app: app-scheduling + template: + metadata: + labels: + app: app-scheduling + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: disk + operator: In + values: + - ssd + containers: + - name: nginx + image: nginx +EOF + +echo "Implementing Question 6: Pod Security Policy" +kubectl label namespace security pod-security.kubernetes.io/enforce=restricted pod-security.kubernetes.io/enforce-version=latest + +cat <<'EOF' | kubectl apply -f - +apiVersion: v1 +kind: Pod +metadata: + name: secure-pod + namespace: security +spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault + containers: + - name: nginx + image: nginx + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1000 + capabilities: + drop: + - ALL + volumes: + - name: html + emptyDir: {} +EOF + +echo "Implementing Question 7: Node Taints and Tolerations" +kubectl taint node k3d-cluster-agent-1 special-workload=true:NoSchedule + +cat <<'EOF' | kubectl apply -f - +apiVersion: apps/v1 +kind: Deployment +metadata: + name: toleration-deploy + namespace: scheduling +spec: + replicas: 2 + selector: + matchLabels: + app: toleration-deploy + template: + metadata: + labels: + app: toleration-deploy + spec: + tolerations: + - key: "special-workload" + operator: "Equal" + value: "true" + effect: "NoSchedule" + containers: + - name: nginx + image: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: normal-deploy + namespace: scheduling +spec: + replicas: 2 + selector: + matchLabels: + app: normal-deploy + template: + metadata: + labels: + app: normal-deploy + spec: + containers: + - name: nginx + image: nginx +EOF + +echo "Implementing Question 8: StatefulSet and Headless Service" + +cat <<'EOF' | kubectl apply -f - +apiVersion: v1 +kind: Service +metadata: + name: web-svc + namespace: stateful +spec: + clusterIP: None + selector: + app: web + ports: + - port: 80 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: web + namespace: stateful +spec: + serviceName: web-svc + replicas: 3 + selector: + matchLabels: + app: web + template: + metadata: + labels: + app: web + spec: + containers: + - name: nginx + image: nginx + volumeMounts: + - name: www + mountPath: /usr/share/nginx/html + volumeClaimTemplates: + - metadata: + name: www + spec: + accessModes: ["ReadWriteOnce"] + storageClassName: cold + resources: + requests: + storage: 1Gi +EOF + +echo "Implementing Question 9: DNS Configuration and Debugging" +cat <<'EOF' | kubectl apply -f - +apiVersion: apps/v1 +kind: Deployment +metadata: + name: web-app + namespace: dns-debug +spec: + replicas: 3 + selector: + matchLabels: + app: web-app + template: + metadata: + labels: + app: web-app + spec: + containers: + - name: nginx + image: nginx +--- +apiVersion: v1 +kind: Service +metadata: + name: web-svc + namespace: dns-debug +spec: + selector: + app: web-app + ports: + - port: 80 +--- +apiVersion: v1 +kind: Pod +metadata: + name: dns-test + namespace: dns-debug +spec: + containers: + - name: busybox + image: busybox + command: + - sh + - -c + - "wget -qO- http://web-svc && wget -qO- http://web-svc.dns-debug.svc.cluster.local && sleep 36000" + dnsConfig: + searches: + - dns-debug.svc.cluster.local + - svc.cluster.local + - cluster.local +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: dns-config + namespace: dns-debug +data: + search-domains: | + search dns-debug.svc.cluster.local svc.cluster.local cluster.local +EOF + +echo "Implementing Question 10: Set up basic DNS service discovery" +echo "Implementing solution for Question 10" +kubectl create namespace dns-config + +kubectl apply -f - < /tmp/dns-test.txt + nslookup dns-svc.dns-config.svc.cluster.local >> /tmp/dns-test.txt + sleep 3600 +EOF + +echo "Implementing Question 11: Helm Chart Deployment" +helm repo add bitnami https://charts.bitnami.com/bitnami +helm repo update +helm install web-release bitnami/nginx \ + --namespace helm-test \ + --set service.type=NodePort \ + --set replicaCount=2 + +echo "Implementing Question 12: Kustomize Configuration" +mkdir -p /tmp/exam/kustomize/base +mkdir -p /tmp/exam/kustomize/overlays/production + +cat <<'EOF' > /tmp/exam/kustomize/base/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx +spec: + replicas: 2 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx + volumeMounts: + - name: nginx-index + mountPath: /usr/share/nginx/html/ + volumes: + - name: nginx-index + configMap: + name: nginx-config +EOF + +cat <<'EOF' > /tmp/exam/kustomize/base/kustomization.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- deployment.yaml +EOF + +cat <<'EOF' > /tmp/exam/kustomize/overlays/production/kustomization.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: kustomize +bases: +- ../../base +patches: +- patch: | + - op: replace + path: /spec/replicas + value: 3 + target: + kind: Deployment + name: nginx +commonLabels: + environment: production +configMapGenerator: +- name: nginx-config + literals: + - index.html=Welcome to Production +EOF + +kubectl create namespace kustomize +kubectl apply -k /tmp/exam/kustomize/overlays/production/ + +echo "Implementing Question 13: Gateway API Configuration" +cat <<'EOF' | kubectl apply -f - +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: Gateway +metadata: + name: main-gateway + namespace: gateway +spec: + gatewayClassName: standard + listeners: + - name: http + port: 80 + protocol: HTTP +--- +apiVersion: gateway.networking.k8s.io/v1beta1 +kind: HTTPRoute +metadata: + name: app-routes + namespace: gateway +spec: + parentRefs: + - name: main-gateway + rules: + - matches: + - path: + value: /app1 + backendRefs: + - name: app1-svc + port: 8080 + - matches: + - path: + value: /app2 + backendRefs: + - name: app2-svc + port: 8080 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app1 + namespace: gateway +spec: + replicas: 1 + selector: + matchLabels: + app: app1 + template: + metadata: + labels: + app: app1 + spec: + containers: + - name: nginx + image: nginx +--- +apiVersion: v1 +kind: Service +metadata: + name: app1-svc + namespace: gateway +spec: + selector: + app: app1 + ports: + - port: 8080 + targetPort: 80 +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app2 + namespace: gateway +spec: + replicas: 1 + selector: + matchLabels: + app: app2 + template: + metadata: + labels: + app: app2 + spec: + containers: + - name: nginx + image: nginx +--- +apiVersion: v1 +kind: Service +metadata: + name: app2-svc + namespace: gateway +spec: + selector: + app: app2 + ports: + - port: 8080 + targetPort: 80 +EOF + +echo "Implementing Question 14: Resource Limits and Quotas" +cat <<'EOF' | kubectl apply -f - +apiVersion: v1 +kind: LimitRange +metadata: + name: resource-limits + namespace: limits +spec: + limits: + - type: Container + default: + cpu: 200m + memory: 256Mi + defaultRequest: + cpu: 100m + memory: 128Mi + max: + cpu: 500m + memory: 512Mi +--- +apiVersion: v1 +kind: ResourceQuota +metadata: + name: compute-quota + namespace: limits +spec: + hard: + cpu: "2" + memory: 2Gi + pods: "5" +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-limits + namespace: limits +spec: + replicas: 2 + selector: + matchLabels: + app: test-limits + template: + metadata: + labels: + app: test-limits + spec: + containers: + - name: nginx + image: nginx +EOF + +echo "Implementing Question 15: Horizontal Pod Autoscaling" +cat <<'EOF' | kubectl apply -f - +apiVersion: apps/v1 +kind: Deployment +metadata: + name: resource-consumer + namespace: monitoring +spec: + replicas: 3 + selector: + matchLabels: + app: resource-consumer + template: + metadata: + labels: + app: resource-consumer + spec: + containers: + - name: resource-consumer + image: gcr.io/kubernetes-e2e-test-images/resource-consumer:1.5 + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 200m + memory: 256Mi +--- +apiVersion: autoscaling/v1 +kind: HorizontalPodAutoscaler +metadata: + name: resource-consumer + namespace: monitoring +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: resource-consumer + minReplicas: 3 + maxReplicas: 6 + targetCPUUtilizationPercentage: 50 +EOF + +echo "Implementing Question 16: RBAC Configuration" +cat <<'EOF' | kubectl apply -f - +apiVersion: v1 +kind: ServiceAccount +metadata: + name: app-admin + namespace: cluster-admin +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: app-admin + namespace: cluster-admin +rules: +- apiGroups: [""] + resources: ["pods"] + verbs: ["list", "get", "watch"] +- apiGroups: ["apps"] + resources: ["deployments"] + verbs: ["list", "get", "watch", "update"] +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["create", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: app-admin + namespace: cluster-admin +subjects: +- kind: ServiceAccount + name: app-admin + namespace: cluster-admin +roleRef: + kind: Role + name: app-admin + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: v1 +kind: Pod +metadata: + name: admin-pod + namespace: cluster-admin +spec: + serviceAccountName: app-admin + containers: + - name: kubectl + image: bitnami/kubectl:latest + command: ["sleep", "3600"] + volumeMounts: + - name: token + mountPath: /var/run/secrets/kubernetes.io/serviceaccount + volumes: + - name: token + projected: + sources: + - serviceAccountToken: + expirationSeconds: 3600 + audience: kubernetes.default.svc +EOF + +echo "Implementing Question 17: Network Policies" +cat <<'EOF' | kubectl apply -f - +apiVersion: apps/v1 +kind: Deployment +metadata: + name: web + namespace: network +spec: + replicas: 1 + selector: + matchLabels: + app: web + template: + metadata: + labels: + app: web + spec: + containers: + - name: nginx + image: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: api + namespace: network +spec: + replicas: 1 + selector: + matchLabels: + app: api + template: + metadata: + labels: + app: api + spec: + containers: + - name: nginx + image: nginx +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: db + namespace: network +spec: + replicas: 1 + selector: + matchLabels: + app: db + template: + metadata: + labels: + app: db + spec: + containers: + - name: postgres + image: postgres + env: + - name: POSTGRES_HOST_AUTH_METHOD + value: trust +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: web-policy + namespace: network +spec: + podSelector: + matchLabels: + app: web + policyTypes: + - Egress + egress: + - to: + - podSelector: + matchLabels: + app: api +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: api-policy + namespace: network +spec: + podSelector: + matchLabels: + app: api + policyTypes: + - Egress + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app: web + egress: + - to: + - podSelector: + matchLabels: + app: db +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: db-policy + namespace: network +spec: + podSelector: + matchLabels: + app: db + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app: api +EOF + +echo "Implementing Question 18: Rolling Updates" +cat <<'EOF' | kubectl apply -f - +apiVersion: apps/v1 +kind: Deployment +metadata: + name: app-v1 + namespace: upgrade +spec: + replicas: 4 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + selector: + matchLabels: + app: app-v1 + template: + metadata: + labels: + app: app-v1 + spec: + containers: + - name: nginx + image: nginx:1.19 +EOF + +kubectl set image deployment/app-v1 nginx=nginx:1.20 -n upgrade --record +kubectl rollout history deployment app-v1 -n upgrade > /tmp/exam/rollout-history.txt +kubectl rollout undo deployment/app-v1 -n upgrade + +echo "Implementing Question 19: Pod Priority and Anti-affinity" +cat <<'EOF' | kubectl apply -f - +apiVersion: scheduling.k8s.io/v1 +kind: PriorityClass +metadata: + name: high-priority +value: 1000 +--- +apiVersion: scheduling.k8s.io/v1 +kind: PriorityClass +metadata: + name: low-priority +value: 100 +--- +apiVersion: v1 +kind: Pod +metadata: + name: high-priority + namespace: scheduling + labels: + priority: high +spec: + priorityClassName: high-priority + containers: + - name: nginx + image: nginx + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: priority + operator: In + values: + - high + - low + topologyKey: kubernetes.io/hostname +--- +apiVersion: v1 +kind: Pod +metadata: + name: low-priority + namespace: scheduling + labels: + priority: low +spec: + priorityClassName: low-priority + containers: + - name: nginx + image: nginx + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: priority + operator: In + values: + - high + - low + topologyKey: kubernetes.io/hostname +EOF + +echo "Implementing Question 20: Troubleshooting Application" +cat <<'EOF' | kubectl apply -f - +apiVersion: apps/v1 +kind: Deployment +metadata: + name: failing-app + namespace: troubleshoot +spec: + replicas: 3 + selector: + matchLabels: + app: failing-app + template: + metadata: + labels: + app: failing-app + spec: + containers: + - name: nginx + image: nginx:1.25 + ports: + - containerPort: 80 + resources: + limits: + memory: 256Mi + livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 15 + periodSeconds: 10 +EOF + +echo "All solutions have been implemented successfully!" \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/assessment.json b/facilitator/assets/exams/cka/002/assessment.json new file mode 100644 index 0000000..c2646f0 --- /dev/null +++ b/facilitator/assets/exams/cka/002/assessment.json @@ -0,0 +1,597 @@ +{ + "questions": [ + { + "id": "1", + "namespace": "storage-task", + "machineHostname": "ckad9999", + "question": "Create a Dynamic PVC named `data-pvc` with the following specifications:\n\n- Storage Class: `standard`\n- Access Mode: `ReadWriteOnce`\n- Storage Request: `2Gi`\n\nThen create a Pod named `data-pod` using the `nginx` image that mounts this PVC as volume with name `data` at `/usr/share/nginx/html`.\n\nEnsure both the PVC and Pod are in the `storage-task` namespace.", + "concepts": ["storage", "persistent-volumes", "pods"], + "verification": [ + { + "id": "1", + "description": "PVC exists with correct specifications", + "verificationScriptFile": "q1_s1_validate_pvc.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "Pod exists and is running", + "verificationScriptFile": "q1_s2_validate_pod.sh", + "expectedOutput": "0", + "weightage": 1 + }, + { + "id": "3", + "description": "PVC is correctly mounted in the pod", + "verificationScriptFile": "q1_s3_validate_mount.sh", + "expectedOutput": "0", + "weightage": 2 + } + ] + }, + { + "id": "2", + "namespace": "storage-class", + "machineHostname": "ckad9999", + "question": "Create a new StorageClass named `fast-local` with the following specifications:\n\n- Provisioner: `rancher.io/local-path`\n- VolumeBindingMode: `WaitForFirstConsumer`\n- Set it as the `default` StorageClass\n\nNote: Ensure any existing default StorageClass is no longer marked as default.", + "concepts": ["storage", "storage-classes"], + "verification": [ + { + "id": "1", + "description": "StorageClass exists with correct specifications", + "verificationScriptFile": "q2_s1_validate_sc.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "StorageClass is set as default", + "verificationScriptFile": "q2_s2_validate_default.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "No other StorageClass is marked as default", + "verificationScriptFile": "q2_s3_validate_no_other_default.sh", + "expectedOutput": "0", + "weightage": 1 + } + ] + }, + { + "id": "3", + "namespace": "manual-storage", + "machineHostname": "ckad9999", + "question": "Create a PersistentVolume named `manual-pv` with the following specifications:\n\n- Storage: `1Gi`\n- Access Mode: `ReadWriteOnce`\n- Host Path: `/mnt/data`\n- Node Affinity: Must run on node `k3d-cluster-agent-0`\n\nThen create a PersistentVolumeClaim named `manual-pvc` that binds to this PV.\n\nFinally, create a Pod named `manual-pod` using the `busybox` image that mounts this PVC at `/data` and runs the command `sleep 3600`. ", + "concepts": ["storage", "persistent-volumes", "node-affinity"], + "verification": [ + { + "id": "1", + "description": "PV exists with correct specifications", + "verificationScriptFile": "q3_s1_validate_pv.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "PVC exists and is bound to PV", + "verificationScriptFile": "q3_s2_validate_pvc.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "Pod exists, is running, and uses the PVC", + "verificationScriptFile": "q3_s3_validate_pod.sh", + "expectedOutput": "0", + "weightage": 1 + } + ] + }, + { + "id": "4", + "namespace": "scaling", + "machineHostname": "ckad9999", + "question": "Create a Deployment named `scaling-app` with the following specifications:\n\n- Image: `nginx`\n- Initial replicas: `2`\n- Resource requests: CPU: `200m`, Memory: `256Mi`\n- Resource limits: CPU: `500m`, Memory: `512Mi`\n\nThen create a `HorizontalPodAutoscaler` for this deployment:\n- Use apiVersion: `autoscaling/v1`\n- Min replicas: `2`\n- Max replicas: `5`\n- Target CPU utilization: `70%`", + "concepts": ["autoscaling", "deployments", "resource-management"], + "verification": [ + { + "id": "1", + "description": "Deployment exists with correct specifications", + "verificationScriptFile": "q4_s1_validate_deployment.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "HPA exists with correct specifications", + "verificationScriptFile": "q4_s2_validate_hpa.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "Deployment has correct resource configurations", + "verificationScriptFile": "q4_s3_validate_resources.sh", + "expectedOutput": "0", + "weightage": 1 + } + ] + }, + { + "id": "5", + "namespace": "scheduling", + "machineHostname": "ckad9999", + "question": "Create a deployment named `app-scheduling` with `3` replicas using the `nginx` image that must only run on node `k3d-cluster-agent-1` using node affinity (not node selector).\n\nRequirements:\n- Use `requiredDuringSchedulingIgnoredDuringExecution`\n- Match the node by its hostname\n- Label the target node with `disk=ssd` before creating the deployment", + "concepts": ["scheduling", "node-affinity", "deployments"], + "verification": [ + { + "id": "1", + "description": "Node is correctly labeled", + "verificationScriptFile": "q5_s1_validate_node_label.sh", + "expectedOutput": "0", + "weightage": 1 + }, + { + "id": "2", + "description": "Deployment exists with correct node affinity", + "verificationScriptFile": "q5_s2_validate_affinity.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "All pods are running on the correct node", + "verificationScriptFile": "q5_s3_validate_pod_placement.sh", + "expectedOutput": "0", + "weightage": 2 + } + ] + }, + { + "id": "6", + "namespace": "security", + "machineHostname": "ckad9999", + "question": "Configure Pod Security for the `security` namespace and create a secure `nginx` pod with `nginx` image :\n\n1. Create the `security` namespace with Pod Security Admission (PSA) controls\n - Set the namespace to enforce the 'restricted' security profile\n - Use the latest version of the security profile\n\n2. Create a secure pod named `secure-pod` in the `security` namespace with the following specifications:\n - Use the `nginx` image\n - Set the pod-level security context to:\n * Run as a non-root user with UID `1000`\n * Enable `runAsNonRoot`\n\n - Configure container-level security context to:\n * Prevent privilege escalation\n * Run as non-root user (UID `1000`)\n * Drop ALL Linux capabilities\n\n - Add a volume mount:\n * Create an emptyDir volume named 'html'\n * Mount the volume at '/usr/share/nginx/html' \n use default seccomp profile", + "concepts": ["security", "pod-security-policy"], + "verification": [ + { + "id": "1", + "description": "Pod Security Policy exists with correct specifications", + "verificationScriptFile": "q6_s1_validate_psp.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "Pod exists and complies with the policy", + "verificationScriptFile": "q6_s2_validate_pod.sh", + "expectedOutput": "0", + "weightage": 2 + } + ] + }, + { + "id": "7", + "namespace": "scheduling", + "machineHostname": "ckad9999", + "question": "Configure node `k3d-cluster-agent-1` with a taint `key=special-workload`, `value=true` and `effect=NoSchedule`.\n\nThen create a deployment named `toleration-deploy` with `2` replicas using the `nginx` image that can tolerate this taint.\n\nFinally, create another deployment named `normal-deploy` with `2` replicas that should not run on the tainted node.", + "concepts": ["scheduling", "taints", "tolerations"], + "verification": [ + { + "id": "1", + "description": "Node is correctly tainted", + "verificationScriptFile": "q7_s1_validate_taint.sh", + "expectedOutput": "0", + "weightage": 1 + }, + { + "id": "2", + "description": "Toleration deployment exists and pods are scheduled correctly", + "verificationScriptFile": "q7_s2_validate_toleration_deploy.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "Normal deployment exists and pods avoid tainted node", + "verificationScriptFile": "q7_s3_validate_normal_deploy.sh", + "expectedOutput": "0", + "weightage": 2 + } + ] + }, + { + "id": "8", + "namespace": "stateful", + "machineHostname": "ckad9999", + "question": "Create a StatefulSet named `web` with `3` replicas using the `nginx` image. Requirements:\n\n- Create a headless service named `web-svc` to expose the StatefulSet\n- Each pod should have a volume mounted at `/usr/share/nginx/html`\n- Use the StorageClass `cold` for dynamic provisioning\n- Volume claim template should request 1Gi storage\n\nEnsure pods are created in sequence and can be accessed using their stable network identity.", + "concepts": ["statefulsets", "headless-services", "storage"], + "verification": [ + { + "id": "1", + "description": "StatefulSet exists with correct specifications", + "verificationScriptFile": "q8_s1_validate_statefulset.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "Headless service exists and is configured correctly", + "verificationScriptFile": "q8_s2_validate_service.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "PVCs are created and bound correctly", + "verificationScriptFile": "q8_s3_validate_storage.sh", + "expectedOutput": "0", + "weightage": 1 + } + ] + }, + { + "id": "9", + "namespace": "dns-debug", + "machineHostname": "ckad9999", + "question": "Set up DNS service discovery testing environment:\n\n1. Create a deployment named `web-app` with `3` replicas using `nginx` image\n\n2. Create a `ClusterIP` service named `web-svc` to expose the deployment\n\n3. Create a Pod named `dns-test` using the `busybox` image that will:\n - Run the command `wget -qO- http://web-svc && wget -qO- http://web-svc.dns-debug.svc.cluster.local && sleep 36000`\n - Verify it can resolve both the service DNS (`web-svc.dns-debug.svc.cluster.local`)\n - Verify it can resolve pod DNS entries\n4. Create a ConfigMap named `dns-config` with custom search domains for the test pod", + "concepts": ["dns", "service-discovery", "networking"], + "verification": [ + { + "id": "1", + "description": "Deployment and service exist and are correctly configured", + "verificationScriptFile": "q9_s1_validate_deployment_svc.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "DNS test pod exists and can resolve service", + "verificationScriptFile": "q9_s2_validate_dns_resolution.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "DNS configuration is correct with custom search domains", + "verificationScriptFile": "q9_s3_validate_dns_config.sh", + "expectedOutput": "0", + "weightage": 1 + } + ] + }, + { + "id": "10", + "namespace": "dns-config", + "machineHostname": "ckad9999", + "question": "Set up basic DNS service discovery:\n\n1. Create a deployment named `dns-app` with 2 replicas using the `nginx` image\n2. Create a service named `dns-svc` to expose the deployment\n3. Create a Pod named `dns-tester` using the `infoblox/dnstools` image that:\n - Runs the command to test DNS resolution of the service\n - Verifies both service DNS (`dns-svc.dns-config.svc.cluster.local`)\n - Stores the test results in `/tmp/dns-test.txt` inside the pod\n\nNote: The test results should include both the service DNS resolution and FQDN resolution.", + "concepts": ["dns", "service-discovery", "networking"], + "verification": [ + { + "id": "1", + "description": "Deployment and service exist and are correctly configured", + "verificationScriptFile": "q10_s1_validate_deployment.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "DNS resolution works correctly", + "verificationScriptFile": "q10_s2_validate_dns.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "Test results are properly stored", + "verificationScriptFile": "q10_s3_validate_results.sh", + "expectedOutput": "0", + "weightage": 1 + } + ] + }, + { + "id": "11", + "namespace": "helm-test", + "machineHostname": "ckad9999", + "question": "Using Helm:\n\n1. Add the bitnami repository `https://charts.bitnami.com/bitnami`\n2. Install the `nginx` chart from bitnami with release name `web-release` in namespace `helm-test`\n3. Configure the service type as `NodePort` and set the replica count to `2`\n4. Verify the deployment is successful and pods are running", + "concepts": ["helm", "package-management"], + "verification": [ + { + "id": "1", + "description": "Helm repository is added correctly", + "verificationScriptFile": "q11_s1_validate_repo.sh", + "expectedOutput": "0", + "weightage": 1 + }, + { + "id": "2", + "description": "Helm release is installed with correct configuration", + "verificationScriptFile": "q11_s2_validate_release.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "Deployment is running with correct specifications", + "verificationScriptFile": "q11_s3_validate_deployment.sh", + "expectedOutput": "0", + "weightage": 2 + } + ] + }, + { + "id": "12", + "namespace": "kustomize", + "machineHostname": "ckad9999", + "question": "Using Kustomize: in the directory `/tmp/exam/kustomize/` \n\n1. Create a base deployment for `nginx` with `2` replicas\n2. Create an overlay that:\n - Adds a label `environment=production`\n - Increases replicas to `3`\n - Adds a ConfigMap named `nginx-config` with key `index.html` and value `Welcome to Production`, mount config map as volume named `nginx-index`\n3. Apply the overlay to create resources in the `kustomize` namespace", + "concepts": ["kustomize", "configuration-management"], + "verification": [ + { + "id": "1", + "description": "Kustomization files exist with correct structure", + "verificationScriptFile": "q12_s1_validate_files.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "Resources are created with correct overlay modifications", + "verificationScriptFile": "q12_s2_validate_resources.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "ConfigMap is created and mounted correctly", + "verificationScriptFile": "q12_s3_validate_configmap.sh", + "expectedOutput": "0", + "weightage": 1 + } + ] + }, + { + "id": "13", + "namespace": "gateway", + "machineHostname": "ckad9999", + "question": "Configure Gateway API resources:\n\n1. Create a Gateway named `main-gateway` listening on port `80`\n2. Create an HTTPRoute to route traffic to a backend service:\n - Path: `/app1` should route to service `app1-svc` port `8080`\n - Path: `/app2` should route to service `app2-svc` port `8080`\n3. Create two deployments (`app1` and `app2`) with corresponding services to test the routing", + "concepts": ["gateway-api", "networking"], + "verification": [ + { + "id": "1", + "description": "Gateway resource exists and is configured correctly", + "verificationScriptFile": "q13_s1_validate_gateway.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "HTTPRoute exists with correct routing rules", + "verificationScriptFile": "q13_s2_validate_httproute.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "Backend services and deployments are running", + "verificationScriptFile": "q13_s3_validate_backends.sh", + "expectedOutput": "0", + "weightage": 1 + } + ] + }, + { + "id": "14", + "namespace": "limits", + "machineHostname": "ckad9999", + "question": "Configure resource management:\n\n1. Create a LimitRange in namespace `limits` with:\n - Default request: CPU: `100m`, Memory: `128Mi`\n - Default limit: CPU: `200m`, Memory: `256Mi`\n - Max limit: CPU: `500m`, Memory: `512Mi`\n\n2. Create a ResourceQuota for the namespace:\n - Max total CPU: `2`\n - Max total memory: `2Gi`\n - Max number of pods: `5`\n\n3. Create a deployment named `test-limits` with `2` replicas to verify the limits are applied", + "concepts": ["resource-management", "limitrange", "resourcequota"], + "verification": [ + { + "id": "1", + "description": "LimitRange exists with correct specifications", + "verificationScriptFile": "q14_s1_validate_limitrange.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "ResourceQuota exists with correct specifications", + "verificationScriptFile": "q14_s2_validate_quota.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "Deployment respects resource constraints", + "verificationScriptFile": "q14_s3_validate_deployment.sh", + "expectedOutput": "0", + "weightage": 1 + } + ] + }, + { + "id": "15", + "namespace": "monitoring", + "machineHostname": "ckad9999", + "question": "Create a deployment named `resource-consumer` with 3 replicas that:\n\n1. Uses the image `gcr.io/kubernetes-e2e-test-images/resource-consumer:1.5`\n2. Sets resource requests:\n - CPU: `100m`\n - Memory: `128Mi`\n3. Sets resource limits:\n - CPU: `200m`\n - Memory: `256Mi`\n4. Create a `HorizontalPodAutoscaler` for this deployment:\n - Min replicas: `3`\n - Max replicas: `6`\n - Target CPU utilization: `50%`\n\nNote: metrics-server is already installed in the cluster.", + "concepts": ["monitoring", "resource-management", "autoscaling"], + "verification": [ + { + "id": "1", + "description": "Deployment exists with correct resource configuration", + "verificationScriptFile": "q15_s1_validate_deployment.sh", + "expectedOutput": "0", + "weightage": 3 + }, + { + "id": "2", + "description": "HPA is configured correctly", + "verificationScriptFile": "q15_s2_validate_hpa.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "Pods are running and ready", + "verificationScriptFile": "q15_s3_validate_pods.sh", + "expectedOutput": "0", + "weightage": 1 + } + ] + }, + { + "id": "16", + "namespace": "cluster-admin", + "machineHostname": "ckad9999", + "question": "Perform the following cluster administration tasks:\n\n1. Create a ServiceAccount named `app-admin` in the `cluster-admin` namespace\n2. Create a Role that allows:\n - `List`, `get`, `watch` operations on `pods` and `deployments`\n - `Create` and `delete` operations on `configmaps`\n - `Update` operations on `deployments`\n3. Bind the Role to the ServiceAccount\n4. Create a test Pod named `admin-pod` that uses this ServiceAccount with:\n - Image: `bitnami/kubectl:latest`\n - Command: `sleep 3600`\n - Mount the ServiceAccount token as a volume\n\nVerify the pod can perform the allowed operations but cannot perform other operations (like creating pods).", + "concepts": ["rbac", "service-accounts", "security"], + "verification": [ + { + "id": "1", + "description": "ServiceAccount exists with correct configuration", + "verificationScriptFile": "q16_s1_validate_sa.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "Role and RoleBinding are configured correctly", + "verificationScriptFile": "q16_s2_validate_rbac.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "Test pod is running with correct ServiceAccount", + "verificationScriptFile": "q16_s3_validate_pod.sh", + "expectedOutput": "0", + "weightage": 2 + } + ] + }, + { + "id": "17", + "namespace": "network", + "machineHostname": "ckad9999", + "question": "Configure network policies:\n\n1. Create a deployment named `web` using `nginx` image with label `app=web`\n2. Create a deployment named `api` using `nginx` image with label `app=api`\n3. Create a deployment named `db` using `postgres` image with label `app=db` and environment variable `POSTGRES_HOST_AUTH_METHOD=trust`\n4. Create NetworkPolicies to:\n - Allow web to communicate only with api\n - Allow api to communicate only with db\n - Deny all other traffic between pods", + "concepts": ["networking", "network-policies", "security"], + "verification": [ + { + "id": "1", + "description": "Deployments exist with correct labels", + "verificationScriptFile": "q17_s1_validate_deployments.sh", + "expectedOutput": "0", + "weightage": 1 + }, + { + "id": "2", + "description": "Network policies exist with correct specifications", + "verificationScriptFile": "q17_s2_validate_policies.sh", + "expectedOutput": "0", + "weightage": 2 + } + ] + }, + { + "id": "18", + "namespace": "upgrade", + "machineHostname": "ckad9999", + "question": "Perform a rolling update:\n\n1. Create a deployment named `app-v1` with `4` replicas using `nginx:1.19`\n2. Perform a rolling update to `nginx:1.20` with:\n - Max unavailable: 1\n - Max surge: 1\n3. Record the update\n4. Save the output of `kubectl rollout history deployment app-v1 -n upgrade` to `/tmp/exam/rollout-history.txt`\n5. Roll back to the previous version", + "concepts": ["deployments", "rolling-updates", "rollback"], + "verification": [ + { + "id": "1", + "description": "Initial deployment exists with correct specifications", + "verificationScriptFile": "q18_s1_validate_initial.sh", + "expectedOutput": "0", + "weightage": 1 + }, + { + "id": "2", + "description": "Rolling update performed correctly and history saved", + "verificationScriptFile": "q18_s2_validate_update.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "Rollback completed successfully", + "verificationScriptFile": "q18_s3_validate_rollback.sh", + "expectedOutput": "0", + "weightage": 2 + } + ] + }, + { + "id": "19", + "namespace": "scheduling", + "machineHostname": "ckad9999", + "question": "Configure advanced scheduling:\n\n1. Create a pod named `high-priority` with PriorityClass priority `1000`\n2. Create a pod named `low-priority` with PriorityClass priority `100`\n3. Configure pod anti-affinity to ensure these pods don't run on the same node\n4. Create enough resource pressure to trigger pod eviction and observe priority behavior\n\nNote for Testing Resource Pressure:\n- Use a resource-intensive image like `polinux/stress` to generate load\n- Example: Create a pod with command: `stress -c 4 -m 2 --vm-bytes 1G` to consume CPU and memory\n- Deploy multiple instances of resource-heavy pods to simulate node resource exhaustion", + "concepts": ["scheduling", "priority", "anti-affinity"], + "verification": [ + { + "id": "1", + "description": "PriorityClasses exist with correct priorities", + "verificationScriptFile": "q19_s1_validate_priority.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "Pods are created with correct specifications", + "verificationScriptFile": "q19_s2_validate_pods.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "Anti-affinity rules are enforced correctly", + "verificationScriptFile": "q19_s3_validate_antiaffinity.sh", + "expectedOutput": "0", + "weightage": 1 + } + ] + }, + { + "id": "20", + "namespace": "troubleshoot", + "machineHostname": "ckad9999", + "question": "A deployment named `failing-app` has been created in the namespace `troubleshoot` but pods are not running. The deployment uses image `nginx:1.25` and should have 3 replicas.\n\nFix the following issues:\n\n1. The deployment is using an incorrect container port (port 8080 instead of 80)\n2. The pods are failing due to insufficient memory (current limit is 64Mi, should be 256Mi)\n3. There's a misconfigured liveness probe checking port 8080 instead of 80\n\nEnsure all pods are running successfully after applying the fixes.", + "concepts": ["troubleshooting", "debugging", "deployment-configuration"], + "verification": [ + { + "id": "1", + "description": "Container port is correctly set to 80", + "verificationScriptFile": "q20_s1_validate_port.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "2", + "description": "Memory limits are set correctly to 256Mi", + "verificationScriptFile": "q20_s2_validate_memory.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "3", + "description": "Liveness probe is configured correctly", + "verificationScriptFile": "q20_s3_validate_probe.sh", + "expectedOutput": "0", + "weightage": 2 + }, + { + "id": "4", + "description": "All pods are running successfully", + "verificationScriptFile": "q20_s4_validate_pods.sh", + "expectedOutput": "0", + "weightage": 2 + } + ] + } + ] +} \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/config.json b/facilitator/assets/exams/cka/002/config.json new file mode 100644 index 0000000..6e81621 --- /dev/null +++ b/facilitator/assets/exams/cka/002/config.json @@ -0,0 +1,10 @@ +{ + "lab": "cka-002", + "workerNodes": 2, + "answers": "assets/exams/cka/002/answers.md", + "questions": "assessment.json", + "totalMarks": 100, + "lowScore": 60, + "mediumScore": 75, + "highScore": 85 +} \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/setup/q10_setup.sh b/facilitator/assets/exams/cka/002/scripts/setup/q10_setup.sh new file mode 100644 index 0000000..0fdb617 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/setup/q10_setup.sh @@ -0,0 +1,10 @@ +#!/bin/bash +set -e + +# Create namespace +kubectl create namespace dns-config --dry-run=client -o yaml | kubectl apply -f - + +# Create directory for test results if it doesn't exist +mkdir -p /tmp/dns-test + +echo "Setup completed for Question 10" \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/setup/q11_setup.sh b/facilitator/assets/exams/cka/002/scripts/setup/q11_setup.sh new file mode 100644 index 0000000..73ebf1c --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/setup/q11_setup.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -e + +# Create namespace +kubectl create namespace helm-test --dry-run=client -o yaml | kubectl apply -f - + +# Ensure helm is installed +helm version || { + echo "Helm is not installed" + exit 1 +} + +# Remove bitnami repo if it exists (to test adding it) +helm repo remove bitnami 2>/dev/null || true + +echo "Setup completed for Question 11" \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/setup/q12_setup.sh b/facilitator/assets/exams/cka/002/scripts/setup/q12_setup.sh new file mode 100644 index 0000000..debce12 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/setup/q12_setup.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -e + +# Create namespace +kubectl create namespace kustomize --dry-run=client -o yaml | kubectl apply -f - + +# Create directory structure for kustomize +mkdir -p /tmp/exam/kustomize/{base,overlays/production} + +# Create initial base files +cat > /tmp/exam/kustomize/base/kustomization.yaml </dev/null || true +kubectl delete resourcequota --all -n limits 2>/dev/null || true + +echo "Setup completed for Question 14" \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/setup/q15_setup.sh b/facilitator/assets/exams/cka/002/scripts/setup/q15_setup.sh new file mode 100644 index 0000000..007d6b0 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/setup/q15_setup.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -e + +# Create namespace +kubectl create namespace monitoring --dry-run=client -o yaml | kubectl apply -f - + +# Pre-pull the resource consumer image to speed up deployment +kubectl run pull-resource-consumer --image=gcr.io/kubernetes-e2e-test-images/resource-consumer:1.5 -n monitoring --dry-run=client -o yaml | kubectl apply -f - + +# Wait for the pull pod to complete +sleep 10 + +# Clean up the pull pod +kubectl delete pod pull-resource-consumer -n monitoring 2>/dev/null || true + +echo "Setup completed for Question 15" \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/setup/q16_setup.sh b/facilitator/assets/exams/cka/002/scripts/setup/q16_setup.sh new file mode 100644 index 0000000..0a8642c --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/setup/q16_setup.sh @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +# Create namespace +kubectl create namespace cluster-admin --dry-run=client -o yaml | kubectl apply -f - + +echo "Setup completed for Question 16" \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/setup/q17_setup.sh b/facilitator/assets/exams/cka/002/scripts/setup/q17_setup.sh new file mode 100644 index 0000000..5ae6e9f --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/setup/q17_setup.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -e + +# Create namespace +kubectl create namespace network --dry-run=client -o yaml | kubectl apply -f - + +# Ensure NetworkPolicy API is enabled +kubectl get networkpolicies -n network || { + echo "NetworkPolicy API is not enabled" + exit 1 +} + +# Delete any existing network policies +kubectl delete networkpolicy --all -n network 2>/dev/null || true + +echo "Setup completed for Question 17" \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/setup/q18_setup.sh b/facilitator/assets/exams/cka/002/scripts/setup/q18_setup.sh new file mode 100644 index 0000000..7596c0a --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/setup/q18_setup.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -e + +# Create namespace +kubectl create namespace upgrade --dry-run=client -o yaml | kubectl apply -f - + +# Create directory for rollout history +mkdir -p /tmp/exam + +# Clean up the pull pods +kubectl delete pod pull-nginx-1-19 pull-nginx-1-20 -n upgrade + +echo "Setup completed for Question 18" \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/setup/q19_setup.sh b/facilitator/assets/exams/cka/002/scripts/setup/q19_setup.sh new file mode 100644 index 0000000..67836aa --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/setup/q19_setup.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -e + +# Create namespace (reusing scheduling namespace) +kubectl create namespace scheduling --dry-run=client -o yaml | kubectl apply -f - + +# Delete any existing PriorityClasses +kubectl delete priorityclass high-priority low-priority 2>/dev/null || true + +# # Create the PriorityClasses +# kubectl create -f - </dev/null || echo "not found") +if [ "$DEPLOY_STATUS" = "not found" ]; then + echo "Deployment dns-app not found" + exit 1 +fi + +REPLICAS=$(echo $DEPLOY_STATUS | cut -d',' -f1) +AVAILABLE=$(echo $DEPLOY_STATUS | cut -d',' -f2) + +if [ "$REPLICAS" != "2" ] || [ "$AVAILABLE" != "2" ]; then + echo "Deployment does not have correct number of replicas" + exit 1 +fi + +# Check if service exists and has correct port +SVC_PORT=$(kubectl get svc dns-svc -n dns-config -o jsonpath='{.spec.ports[0].port}' 2>/dev/null || echo "not found") +if [ "$SVC_PORT" = "not found" ]; then + echo "Service dns-svc not found" + exit 1 +fi + +if [ "$SVC_PORT" != "80" ]; then + echo "Service port is not configured correctly" + exit 1 +fi + +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q10_s2_validate_dns.sh b/facilitator/assets/exams/cka/002/scripts/validation/q10_s2_validate_dns.sh new file mode 100644 index 0000000..39dd7cd --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q10_s2_validate_dns.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -e + +# Check if tester pod exists and is running +POD_STATUS=$(kubectl get pod dns-tester -n dns-config -o jsonpath='{.status.phase}' 2>/dev/null || echo "not found") +if [ "$POD_STATUS" = "not found" ]; then + echo "DNS tester pod not found" + exit 1 +fi + +if [ "$POD_STATUS" != "Running" ]; then + echo "DNS tester pod is not running" + exit 1 +fi + +# Test DNS resolution +TEST_RESULT=$(kubectl exec -n dns-config dns-tester -- nslookup dns-svc 2>/dev/null || echo "failed") +if echo "$TEST_RESULT" | grep -q "failed"; then + echo "DNS resolution test failed" + exit 1 +fi + +# Test FQDN resolution +TEST_RESULT=$(kubectl exec -n dns-config dns-tester -- nslookup dns-svc.dns-config.svc.cluster.local 2>/dev/null || echo "failed") +if echo "$TEST_RESULT" | grep -q "failed"; then + echo "FQDN DNS resolution test failed" + exit 1 +fi + +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q10_s3_validate_results.sh b/facilitator/assets/exams/cka/002/scripts/validation/q10_s3_validate_results.sh new file mode 100644 index 0000000..064001a --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q10_s3_validate_results.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e + +# Check if results file exists in the pod +FILE_CHECK=$(kubectl exec -n dns-config dns-tester -- test -f /tmp/dns-test.txt && echo "exists" || echo "not found") +if [ "$FILE_CHECK" = "not found" ]; then + echo "DNS test results file not found" + exit 1 +fi + +# Check if file has content +CONTENT=$(kubectl exec -n dns-config dns-tester -- cat /tmp/dns-test.txt 2>/dev/null || echo "") +if [ -z "$CONTENT" ]; then + echo "DNS test results file is empty" + exit 1 +fi + +# Verify file contains required information +if ! echo "$CONTENT" | grep -q "dns-svc.dns-config.svc.cluster.local"; then + echo "FQDN resolution results not found in file" + exit 1 +fi + +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q11_s1_validate_repo.sh b/facilitator/assets/exams/cka/002/scripts/validation/q11_s1_validate_repo.sh new file mode 100644 index 0000000..30b5009 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q11_s1_validate_repo.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e + +# Check if bitnami repo is added +helm repo list | grep bitnami || { + echo "Bitnami repository not found in helm repo list" + exit 1 +} + +# Check if repo URL is correct +REPO_URL=$(helm repo list | grep bitnami | awk '{print $2}') +if [[ "$REPO_URL" != "https://charts.bitnami.com/bitnami" ]]; then + echo "Incorrect repository URL. Expected https://charts.bitnami.com/bitnami, got $REPO_URL" + exit 1 +fi + +# Check if repo is up to date +helm repo update bitnami || { + echo "Failed to update bitnami repository" + exit 1 +} + +echo "Helm repository validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q11_s2_validate_release.sh b/facilitator/assets/exams/cka/002/scripts/validation/q11_s2_validate_release.sh new file mode 100644 index 0000000..b62c400 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q11_s2_validate_release.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e + +# Check if release exists +helm status web-release -n helm-test || { + echo "Helm release web-release not found in namespace helm-test" + exit 1 +} + +# Check if it's using nginx chart +CHART=$(helm get manifest web-release -n helm-test | grep "chart:" | head -1) +if [[ ! "$CHART" =~ "nginx" ]]; then + echo "Release is not using nginx chart" + exit 1 +fi + +# Check if service type is NodePort +SERVICE_TYPE=$(kubectl get service web-release-nginx -n helm-test -o jsonpath='{.spec.type}') +if [[ "$SERVICE_TYPE" != "NodePort" ]]; then + echo "Service type is not NodePort. Current type: $SERVICE_TYPE" + exit 1 +fi + +# Check replica count +REPLICAS=$(kubectl get deployment web-release-nginx -n helm-test -o jsonpath='{.spec.replicas}') +if [[ "$REPLICAS" != "2" ]]; then + echo "Incorrect number of replicas. Expected 2, got $REPLICAS" + exit 1 +fi + +echo "Helm release validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q11_s3_validate_deployment.sh b/facilitator/assets/exams/cka/002/scripts/validation/q11_s3_validate_deployment.sh new file mode 100644 index 0000000..d3afcbd --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q11_s3_validate_deployment.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment web-release-nginx -n helm-test || { + echo "Deployment web-release-nginx not found in namespace helm-test" + exit 1 +} + +# Check if all pods are running +READY_PODS=$(kubectl get deployment web-release-nginx -n helm-test -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "2" ]]; then + echo "Not all pods are ready. Expected 2, got $READY_PODS" + exit 1 +fi + +# Check if pods are using correct image +POD_IMAGE=$(kubectl get deployment web-release-nginx -n helm-test -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ ! "$POD_IMAGE" =~ "nginx" ]]; then + echo "Pods are not using nginx image. Current image: $POD_IMAGE" + exit 1 +fi + +# Check if service is accessible +SERVICE_PORT=$(kubectl get service web-release-nginx -n helm-test -o jsonpath='{.spec.ports[0].nodePort}') +if [[ -z "$SERVICE_PORT" ]]; then + echo "NodePort not configured for service" + exit 1 +fi + +echo "Deployment validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q12_s1_validate_files.sh b/facilitator/assets/exams/cka/002/scripts/validation/q12_s1_validate_files.sh new file mode 100644 index 0000000..c114ecd --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q12_s1_validate_files.sh @@ -0,0 +1,52 @@ +#!/bin/bash +set -e + +# Check base directory structure +BASE_DIR="/tmp/exam/kustomize/base" +if [[ ! -d "$BASE_DIR" ]]; then + echo "Base directory not found at $BASE_DIR" + exit 1 +fi + +# Check overlay directory structure +OVERLAY_DIR="/tmp/exam/kustomize/overlays/production" +if [[ ! -d "$OVERLAY_DIR" ]]; then + echo "Overlay directory not found at $OVERLAY_DIR" + exit 1 +fi + +# Check base kustomization.yaml +if [[ ! -f "$BASE_DIR/kustomization.yaml" ]]; then + echo "Base kustomization.yaml not found" + exit 1 +fi + +# Check base deployment.yaml +if [[ ! -f "$BASE_DIR/deployment.yaml" ]]; then + echo "Base deployment.yaml not found" + exit 1 +fi + +# Check overlay kustomization.yaml +if [[ ! -f "$OVERLAY_DIR/kustomization.yaml" ]]; then + echo "Overlay kustomization.yaml not found" + exit 1 +fi + +# Validate base kustomization.yaml content +BASE_RESOURCES=$(cat "$BASE_DIR/kustomization.yaml" | grep "deployment.yaml") +if [[ -z "$BASE_RESOURCES" ]]; then + echo "Base kustomization.yaml does not reference deployment.yaml" + exit 1 +fi + +# Validate overlay kustomization.yaml content +OVERLAY_CONTENT=$(cat "$OVERLAY_DIR/kustomization.yaml") +if [[ ! "$OVERLAY_CONTENT" =~ "production" ]] || \ + [[ ! "$OVERLAY_CONTENT" =~ "value: 3" ]]; then + echo "Overlay kustomization.yaml missing required configurations" + exit 1 +fi + +echo "Kustomize files validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q12_s2_validate_resources.sh b/facilitator/assets/exams/cka/002/scripts/validation/q12_s2_validate_resources.sh new file mode 100644 index 0000000..664a1e0 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q12_s2_validate_resources.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment nginx -n kustomize || { + echo "Deployment nginx not found in namespace kustomize" + exit 1 +} + +# Check replicas +REPLICAS=$(kubectl get deployment nginx -n kustomize -o jsonpath='{.spec.replicas}') +if [[ "$REPLICAS" != "3" ]]; then + echo "Incorrect number of replicas. Expected 3, got $REPLICAS" + exit 1 +fi + +# Check environment label +ENV_LABEL=$(kubectl get deployment nginx -n kustomize -o jsonpath='{.metadata.labels.environment}') +if [[ "$ENV_LABEL" != "production" ]]; then + echo "Incorrect environment label. Expected production, got $ENV_LABEL" + exit 1 +fi + +# Check if pods are running +READY_PODS=$(kubectl get deployment nginx -n kustomize -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "3" ]]; then + echo "Not all pods are ready. Expected 3, got $READY_PODS" + exit 1 +fi + +# Check if pods have the environment label +POD_LABEL=$(kubectl get pods -n kustomize -l app=nginx -o jsonpath='{.items[0].metadata.labels.environment}') +if [[ "$POD_LABEL" != "production" ]]; then + echo "Pods do not have correct environment label" + exit 1 +fi + +echo "Resources validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q12_s3_validate_configmap.sh b/facilitator/assets/exams/cka/002/scripts/validation/q12_s3_validate_configmap.sh new file mode 100644 index 0000000..5b6d746 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q12_s3_validate_configmap.sh @@ -0,0 +1,38 @@ +#!/bin/bash +set -e + +NAMESPACE="kustomize" + +# Find the full name of the nginx-config ConfigMap +CONFIGMAP_NAME=$(kubectl get configmap -n $NAMESPACE --no-headers | awk '/^nginx-config-/ {print $1; exit}') + +if [[ -z "$CONFIGMAP_NAME" ]]; then + echo "ConfigMap starting with nginx-config not found in namespace $NAMESPACE" + exit 1 +fi + +echo "Found ConfigMap: $CONFIGMAP_NAME" + +# Check ConfigMap content +CONFIG_CONTENT=$(kubectl get configmap "$CONFIGMAP_NAME" -n $NAMESPACE -o jsonpath='{.data.index\.html}') +if [[ "$CONFIG_CONTENT" != "Welcome to Production" ]]; then + echo "Incorrect ConfigMap content. Expected 'Welcome to Production', got '$CONFIG_CONTENT'" + exit 1 +fi + +# Check if ConfigMap is mounted in pods +MOUNT_PATH=$(kubectl get deployment nginx -n $NAMESPACE -o jsonpath='{.spec.template.spec.containers[0].volumeMounts[?(@.name=="nginx-index")].mountPath}') +if [[ -z "$MOUNT_PATH" ]]; then + echo "ConfigMap not mounted in deployment" + exit 1 +fi + +# Check if volume is configured correctly +VOLUME_NAME=$(kubectl get deployment nginx -n $NAMESPACE -o jsonpath="{.spec.template.spec.volumes[?(@.configMap.name==\"$CONFIGMAP_NAME\")].name}") +if [[ -z "$VOLUME_NAME" ]]; then + echo "ConfigMap volume not configured correctly in deployment" + exit 1 +fi + +echo "ConfigMap validation successful" +exit 0 diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q13_s1_validate_gateway.sh b/facilitator/assets/exams/cka/002/scripts/validation/q13_s1_validate_gateway.sh new file mode 100644 index 0000000..eaced31 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q13_s1_validate_gateway.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e + +# Check if Gateway exists +kubectl get gateway main-gateway -n gateway || { + echo "Gateway main-gateway not found in namespace gateway" + exit 1 +} + +# Check if Gateway is listening on port 80 +PORT=$(kubectl get gateway main-gateway -n gateway -o jsonpath='{.spec.listeners[0].port}') +if [[ "$PORT" != "80" ]]; then + echo "Gateway is not listening on port 80. Current port: $PORT" + exit 1 +fi + +# Check protocol +PROTOCOL=$(kubectl get gateway main-gateway -n gateway -o jsonpath='{.spec.listeners[0].protocol}') +if [[ "$PROTOCOL" != "HTTP" ]]; then + echo "Gateway is not using HTTP protocol. Current protocol: $PROTOCOL" + exit 1 +fi + +# Check if Gateway is ready +# READY_STATUS=$(kubectl get gateway main-gateway -n gateway -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}') +# if [[ "$READY_STATUS" != "True" ]]; then +# echo "Gateway is not ready. Current status: $READY_STATUS" +# exit 1 +# fi + +echo "Gateway validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q13_s2_validate_httproute.sh b/facilitator/assets/exams/cka/002/scripts/validation/q13_s2_validate_httproute.sh new file mode 100644 index 0000000..6eede70 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q13_s2_validate_httproute.sh @@ -0,0 +1,46 @@ +#!/bin/bash +set -e + +# Check if HTTPRoute exists +kubectl get httproute -n gateway || { + echo "No HTTPRoute found in namespace gateway" + exit 1 +} + +# Check app1 path rule +APP1_PATH=$(kubectl get httproute -n gateway -o jsonpath='{.items[0].spec.rules[?(@.matches[0].path.value=="/app1")].matches[0].path.value}') +if [[ "$APP1_PATH" != "/app1" ]]; then + echo "Missing or incorrect path rule for /app1" + exit 1 +fi + +# Check app2 path rule +APP2_PATH=$(kubectl get httproute -n gateway -o jsonpath='{.items[0].spec.rules[?(@.matches[0].path.value=="/app2")].matches[0].path.value}') +if [[ "$APP2_PATH" != "/app2" ]]; then + echo "Missing or incorrect path rule for /app2" + exit 1 +fi + +# Check backend services +APP1_BACKEND=$(kubectl get httproute -n gateway -o jsonpath='{.items[0].spec.rules[?(@.matches[0].path.value=="/app1")].backendRefs[0].name}') +if [[ "$APP1_BACKEND" != "app1-svc" ]]; then + echo "Incorrect backend service for /app1. Expected app1-svc, got $APP1_BACKEND" + exit 1 +fi + +APP2_BACKEND=$(kubectl get httproute -n gateway -o jsonpath='{.items[0].spec.rules[?(@.matches[0].path.value=="/app2")].backendRefs[0].name}') +if [[ "$APP2_BACKEND" != "app2-svc" ]]; then + echo "Incorrect backend service for /app2. Expected app2-svc, got $APP2_BACKEND" + exit 1 +fi + +# Check backend ports +APP1_PORT=$(kubectl get httproute -n gateway -o jsonpath='{.items[0].spec.rules[?(@.matches[0].path.value=="/app1")].backendRefs[0].port}') +APP2_PORT=$(kubectl get httproute -n gateway -o jsonpath='{.items[0].spec.rules[?(@.matches[0].path.value=="/app2")].backendRefs[0].port}') +if [[ "$APP1_PORT" != "8080" ]] || [[ "$APP2_PORT" != "8080" ]]; then + echo "Incorrect backend ports. Expected 8080 for both services" + exit 1 +fi + +echo "HTTPRoute validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q13_s3_validate_backends.sh b/facilitator/assets/exams/cka/002/scripts/validation/q13_s3_validate_backends.sh new file mode 100644 index 0000000..8a0e30e --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q13_s3_validate_backends.sh @@ -0,0 +1,52 @@ +#!/bin/bash +set -e + +# Check app1 deployment and service +kubectl get deployment app1 -n gateway || { + echo "Deployment app1 not found in namespace gateway" + exit 1 +} + +kubectl get service app1-svc -n gateway || { + echo "Service app1-svc not found in namespace gateway" + exit 1 +} + +# Check app2 deployment and service +kubectl get deployment app2 -n gateway || { + echo "Deployment app2 not found in namespace gateway" + exit 1 +} + +kubectl get service app2-svc -n gateway || { + echo "Service app2-svc not found in namespace gateway" + exit 1 +} + +# Check if services are configured correctly +for SVC in app1-svc app2-svc; do + # Check port + PORT=$(kubectl get service $SVC -n gateway -o jsonpath='{.spec.ports[0].port}') + if [[ "$PORT" != "8080" ]]; then + echo "Service $SVC has incorrect port. Expected 8080, got $PORT" + exit 1 + fi + + # Check if pods are running + APP_NAME=${SVC%-svc} + READY_PODS=$(kubectl get deployment $APP_NAME -n gateway -o jsonpath='{.status.readyReplicas}') + if [[ -z "$READY_PODS" ]] || [[ "$READY_PODS" -lt 1 ]]; then + echo "No ready pods found for deployment $APP_NAME" + exit 1 + fi + + # Check if service endpoints exist + ENDPOINTS=$(kubectl get endpoints $SVC -n gateway -o jsonpath='{.subsets[0].addresses}') + if [[ -z "$ENDPOINTS" ]]; then + echo "No endpoints found for service $SVC" + exit 1 + fi +done + +echo "Backend services validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q14_s1_validate_limitrange.sh b/facilitator/assets/exams/cka/002/scripts/validation/q14_s1_validate_limitrange.sh new file mode 100644 index 0000000..51a8ee3 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q14_s1_validate_limitrange.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -e + +# Check if LimitRange exists +kubectl get limitrange -n limits || { + echo "No LimitRange found in namespace limits" + exit 1 +} + +# Check default request +CPU_REQUEST=$(kubectl get limitrange -n limits -o jsonpath='{.items[0].spec.limits[?(@.type=="Container")].defaultRequest.cpu}') +if [[ "$CPU_REQUEST" != "100m" ]]; then + echo "Incorrect default CPU request. Expected 100m, got $CPU_REQUEST" + exit 1 +fi + +MEM_REQUEST=$(kubectl get limitrange -n limits -o jsonpath='{.items[0].spec.limits[?(@.type=="Container")].defaultRequest.memory}') +if [[ "$MEM_REQUEST" != "128Mi" ]]; then + echo "Incorrect default memory request. Expected 128Mi, got $MEM_REQUEST" + exit 1 +fi + +# Check default limit +CPU_LIMIT=$(kubectl get limitrange -n limits -o jsonpath='{.items[0].spec.limits[?(@.type=="Container")].default.cpu}') +if [[ "$CPU_LIMIT" != "200m" ]]; then + echo "Incorrect default CPU limit. Expected 200m, got $CPU_LIMIT" + exit 1 +fi + +MEM_LIMIT=$(kubectl get limitrange -n limits -o jsonpath='{.items[0].spec.limits[?(@.type=="Container")].default.memory}') +if [[ "$MEM_LIMIT" != "256Mi" ]]; then + echo "Incorrect default memory limit. Expected 256Mi, got $MEM_LIMIT" + exit 1 +fi + +# Check max limit +MAX_CPU=$(kubectl get limitrange -n limits -o jsonpath='{.items[0].spec.limits[?(@.type=="Container")].max.cpu}') +if [[ "$MAX_CPU" != "500m" ]]; then + echo "Incorrect max CPU limit. Expected 500m, got $MAX_CPU" + exit 1 +fi + +MAX_MEM=$(kubectl get limitrange -n limits -o jsonpath='{.items[0].spec.limits[?(@.type=="Container")].max.memory}') +if [[ "$MAX_MEM" != "512Mi" ]]; then + echo "Incorrect max memory limit. Expected 512Mi, got $MAX_MEM" + exit 1 +fi + +echo "LimitRange validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q14_s2_validate_quota.sh b/facilitator/assets/exams/cka/002/scripts/validation/q14_s2_validate_quota.sh new file mode 100644 index 0000000..7a8e1cc --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q14_s2_validate_quota.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +# Check if ResourceQuota exists +kubectl get resourcequota -n limits || { + echo "No ResourceQuota found in namespace limits" + exit 1 +} + +# Check CPU quota +CPU_QUOTA=$(kubectl get resourcequota -n limits -o jsonpath='{.items[0].spec.hard.cpu}') +if [[ "$CPU_QUOTA" != "2" ]]; then + echo "Incorrect CPU quota. Expected 2, got $CPU_QUOTA" + exit 1 +fi + +# Check memory quota +MEM_QUOTA=$(kubectl get resourcequota -n limits -o jsonpath='{.items[0].spec.hard.memory}') +if [[ "$MEM_QUOTA" != "2Gi" ]]; then + echo "Incorrect memory quota. Expected 2Gi, got $MEM_QUOTA" + exit 1 +fi + +# Check pod quota +POD_QUOTA=$(kubectl get resourcequota -n limits -o jsonpath='{.items[0].spec.hard.pods}') +if [[ "$POD_QUOTA" != "5" ]]; then + echo "Incorrect pod quota. Expected 5, got $POD_QUOTA" + exit 1 +fi + +# Check if quota is being enforced +QUOTA_STATUS=$(kubectl get resourcequota -n limits -o jsonpath='{.items[0].status.used}') +if [[ -z "$QUOTA_STATUS" ]]; then + echo "ResourceQuota is not being enforced" + exit 1 +fi + +echo "ResourceQuota validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q14_s3_validate_deployment.sh b/facilitator/assets/exams/cka/002/scripts/validation/q14_s3_validate_deployment.sh new file mode 100644 index 0000000..6fb6cbc --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q14_s3_validate_deployment.sh @@ -0,0 +1,55 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment test-limits -n limits || { + echo "Deployment test-limits not found in namespace limits" + exit 1 +} + +# Check replicas +REPLICAS=$(kubectl get deployment test-limits -n limits -o jsonpath='{.spec.replicas}') +if [[ "$REPLICAS" != "2" ]]; then + echo "Incorrect number of replicas. Expected 2, got $REPLICAS" + exit 1 +fi + +# Check if pods are running +READY_PODS=$(kubectl get deployment test-limits -n limits -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "2" ]]; then + echo "Not all pods are ready. Expected 2, got $READY_PODS" + exit 1 +fi + +# Check if pods respect LimitRange +PODS=$(kubectl get pods -n limits -l app=test-limits -o jsonpath='{.items[*].metadata.name}') +for POD in $PODS; do + # Check resource requests + CPU_REQUEST=$(kubectl get pod $POD -n limits -o jsonpath='{.spec.containers[0].resources.requests.cpu}') + if [[ "$CPU_REQUEST" != "100m" ]]; then + echo "Pod $POD has incorrect CPU request. Expected 100m, got $CPU_REQUEST" + exit 1 + fi + + MEM_REQUEST=$(kubectl get pod $POD -n limits -o jsonpath='{.spec.containers[0].resources.requests.memory}') + if [[ "$MEM_REQUEST" != "128Mi" ]]; then + echo "Pod $POD has incorrect memory request. Expected 128Mi, got $MEM_REQUEST" + exit 1 + fi + + # Check resource limits + CPU_LIMIT=$(kubectl get pod $POD -n limits -o jsonpath='{.spec.containers[0].resources.limits.cpu}') + if [[ "$CPU_LIMIT" != "200m" ]]; then + echo "Pod $POD has incorrect CPU limit. Expected 200m, got $CPU_LIMIT" + exit 1 + fi + + MEM_LIMIT=$(kubectl get pod $POD -n limits -o jsonpath='{.spec.containers[0].resources.limits.memory}') + if [[ "$MEM_LIMIT" != "256Mi" ]]; then + echo "Pod $POD has incorrect memory limit. Expected 256Mi, got $MEM_LIMIT" + exit 1 + fi +done + +echo "Deployment validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q15_s1_validate_deployment.sh b/facilitator/assets/exams/cka/002/scripts/validation/q15_s1_validate_deployment.sh new file mode 100644 index 0000000..9d1a35d --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q15_s1_validate_deployment.sh @@ -0,0 +1,51 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment resource-consumer -n monitoring || { + echo "Deployment resource-consumer not found in namespace monitoring" + exit 1 +} + +# Check replicas +REPLICAS=$(kubectl get deployment resource-consumer -n monitoring -o jsonpath='{.spec.replicas}') +if [[ "$REPLICAS" != "3" ]]; then + echo "Incorrect number of replicas. Expected 3, got $REPLICAS" + exit 1 +fi + +# Check image +IMAGE=$(kubectl get deployment resource-consumer -n monitoring -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ "$IMAGE" != "gcr.io/kubernetes-e2e-test-images/resource-consumer:1.5" ]]; then + echo "Incorrect image. Expected gcr.io/kubernetes-e2e-test-images/resource-consumer:1.5, got $IMAGE" + exit 1 +fi + +# Check resource requests +CPU_REQUEST=$(kubectl get deployment resource-consumer -n monitoring -o jsonpath='{.spec.template.spec.containers[0].resources.requests.cpu}') +if [[ "$CPU_REQUEST" != "100m" ]]; then + echo "Incorrect CPU request. Expected 100m, got $CPU_REQUEST" + exit 1 +fi + +MEM_REQUEST=$(kubectl get deployment resource-consumer -n monitoring -o jsonpath='{.spec.template.spec.containers[0].resources.requests.memory}') +if [[ "$MEM_REQUEST" != "128Mi" ]]; then + echo "Incorrect memory request. Expected 128Mi, got $MEM_REQUEST" + exit 1 +fi + +# Check resource limits +CPU_LIMIT=$(kubectl get deployment resource-consumer -n monitoring -o jsonpath='{.spec.template.spec.containers[0].resources.limits.cpu}') +if [[ "$CPU_LIMIT" != "200m" ]]; then + echo "Incorrect CPU limit. Expected 200m, got $CPU_LIMIT" + exit 1 +fi + +MEM_LIMIT=$(kubectl get deployment resource-consumer -n monitoring -o jsonpath='{.spec.template.spec.containers[0].resources.limits.memory}') +if [[ "$MEM_LIMIT" != "256Mi" ]]; then + echo "Incorrect memory limit. Expected 256Mi, got $MEM_LIMIT" + exit 1 +fi + +echo "Deployment validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q15_s2_validate_hpa.sh b/facilitator/assets/exams/cka/002/scripts/validation/q15_s2_validate_hpa.sh new file mode 100644 index 0000000..2f804c6 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q15_s2_validate_hpa.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +# Check if HPA exists +kubectl get hpa -n monitoring resource-consumer || { + echo "HorizontalPodAutoscaler resource-consumer not found in namespace monitoring" + exit 1 +} + +# Check min replicas +MIN_REPLICAS=$(kubectl get hpa -n monitoring resource-consumer -o jsonpath='{.spec.minReplicas}') +if [[ "$MIN_REPLICAS" != "3" ]]; then + echo "Incorrect minimum replicas. Expected 3, got $MIN_REPLICAS" + exit 1 +fi + +# Check max replicas +MAX_REPLICAS=$(kubectl get hpa -n monitoring resource-consumer -o jsonpath='{.spec.maxReplicas}') +if [[ "$MAX_REPLICAS" != "6" ]]; then + echo "Incorrect maximum replicas. Expected 6, got $MAX_REPLICAS" + exit 1 +fi + +# Check target CPU utilization +TARGET_CPU=$(kubectl get hpa resource-consumer -n monitoring -o jsonpath='{.spec.metrics[0].resource.target.averageUtilization}') +if [[ "$TARGET_CPU" != "50" ]]; then + echo "Incorrect target CPU utilization. Expected 50, got $TARGET_CPU" + exit 1 +fi + +# Check if HPA is targeting the correct deployment +TARGET_REF=$(kubectl get hpa -n monitoring resource-consumer -o jsonpath='{.spec.scaleTargetRef.name}') +if [[ "$TARGET_REF" != "resource-consumer" ]]; then + echo "HPA is not targeting the correct deployment. Expected resource-consumer, got $TARGET_REF" + exit 1 +fi + +echo "HPA validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q15_s3_validate_pods.sh b/facilitator/assets/exams/cka/002/scripts/validation/q15_s3_validate_pods.sh new file mode 100644 index 0000000..f4a99f8 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q15_s3_validate_pods.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -e + +# Check if pods are running +READY_PODS=$(kubectl get deployment resource-consumer -n monitoring -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "3" ]]; then + echo "Not all pods are ready. Expected 3, got $READY_PODS" + exit 1 +fi + +# Get all pods +PODS=$(kubectl get pods -n monitoring -l app=resource-consumer -o jsonpath='{.items[*].metadata.name}') + +# Check each pod's configuration +for POD in $PODS; do + # Check if pod is running + POD_STATUS=$(kubectl get pod $POD -n monitoring -o jsonpath='{.status.phase}') + if [[ "$POD_STATUS" != "Running" ]]; then + echo "Pod $POD is not running. Current status: $POD_STATUS" + exit 1 + fi + + # Check resource requests + CPU_REQUEST=$(kubectl get pod $POD -n monitoring -o jsonpath='{.spec.containers[0].resources.requests.cpu}') + if [[ "$CPU_REQUEST" != "100m" ]]; then + echo "Pod $POD has incorrect CPU request. Expected 100m, got $CPU_REQUEST" + exit 1 + fi + + MEM_REQUEST=$(kubectl get pod $POD -n monitoring -o jsonpath='{.spec.containers[0].resources.requests.memory}') + if [[ "$MEM_REQUEST" != "128Mi" ]]; then + echo "Pod $POD has incorrect memory request. Expected 128Mi, got $MEM_REQUEST" + exit 1 + fi + + # Check if metrics are being collected + kubectl top pod $POD -n monitoring > /dev/null || { + echo "Unable to get metrics for pod $POD" + exit 1 + } +done + +echo "Pods validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q16_s1_validate_sa.sh b/facilitator/assets/exams/cka/002/scripts/validation/q16_s1_validate_sa.sh new file mode 100644 index 0000000..ca093f6 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q16_s1_validate_sa.sh @@ -0,0 +1,31 @@ +#!/bin/bash +set -e + +# Check if ServiceAccount exists +kubectl get serviceaccount app-admin -n cluster-admin || { + echo "ServiceAccount app-admin not found in namespace cluster-admin" + exit 1 +} + +# Check if token is automatically mounted +AUTO_MOUNT=$(kubectl get serviceaccount app-admin -n cluster-admin -o jsonpath='{.automountServiceAccountToken}') +if [[ "$AUTO_MOUNT" == "false" ]]; then + echo "ServiceAccount token automounting is disabled" + exit 1 +fi + +# Check if secret is created for the ServiceAccount +# SECRET_NAME=$(kubectl get serviceaccount app-admin -n cluster-admin -o jsonpath='{.secrets[0].name}') +# if [[ -z "$SECRET_NAME" ]]; then +# echo "No token secret found for ServiceAccount" +# exit 1 +# fi + +# # Verify secret exists +# kubectl get secret $SECRET_NAME -n cluster-admin || { +# echo "Token secret $SECRET_NAME not found" +# exit 1 +# } + +echo "ServiceAccount validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q16_s2_validate_rbac.sh b/facilitator/assets/exams/cka/002/scripts/validation/q16_s2_validate_rbac.sh new file mode 100644 index 0000000..ec90295 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q16_s2_validate_rbac.sh @@ -0,0 +1,54 @@ +#!/bin/bash +set -e + +# Check if Role exists +kubectl get role app-admin -n cluster-admin || { + echo "Role app-admin not found in namespace cluster-admin" + exit 1 +} + +# Check Role permissions for pods and deployments +RULES=$(kubectl get role app-admin -n cluster-admin -o json) + +# Check list, get, watch permissions for pods and deployments +POD_RULES=$(echo "$RULES" | jq -r '.rules[] | select(.resources[] | contains("pods"))') +DEPLOYMENT_RULES=$(echo "$RULES" | jq -r '.rules[] | select(.resources[] | contains("deployments"))') + +if [[ ! "$POD_RULES" =~ "list" ]] || [[ ! "$POD_RULES" =~ "get" ]] || [[ ! "$POD_RULES" =~ "watch" ]]; then + echo "Missing required permissions for pods" + exit 1 +fi + +if [[ ! "$DEPLOYMENT_RULES" =~ "list" ]] || [[ ! "$DEPLOYMENT_RULES" =~ "get" ]] || [[ ! "$DEPLOYMENT_RULES" =~ "watch" ]] || [[ ! "$DEPLOYMENT_RULES" =~ "update" ]]; then + echo "Missing required permissions for deployments" + exit 1 +fi + +# Check configmap permissions +CONFIGMAP_RULES=$(echo "$RULES" | jq -r '.rules[] | select(.resources[] | contains("configmaps"))') +if [[ ! "$CONFIGMAP_RULES" =~ "create" ]] || [[ ! "$CONFIGMAP_RULES" =~ "delete" ]]; then + echo "Missing required permissions for configmaps" + exit 1 +fi + +# Check if RoleBinding exists +kubectl get rolebinding app-admin -n cluster-admin || { + echo "RoleBinding app-admin not found in namespace cluster-admin" + exit 1 +} + +# Check if RoleBinding references correct Role and ServiceAccount +ROLE_REF=$(kubectl get rolebinding app-admin -n cluster-admin -o jsonpath='{.roleRef.name}') +if [[ "$ROLE_REF" != "app-admin" ]]; then + echo "RoleBinding references incorrect Role. Expected app-admin, got $ROLE_REF" + exit 1 +fi + +SA_REF=$(kubectl get rolebinding app-admin -n cluster-admin -o jsonpath='{.subjects[0].name}') +if [[ "$SA_REF" != "app-admin" ]]; then + echo "RoleBinding references incorrect ServiceAccount. Expected app-admin, got $SA_REF" + exit 1 +fi + +echo "RBAC validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q16_s3_validate_pod.sh b/facilitator/assets/exams/cka/002/scripts/validation/q16_s3_validate_pod.sh new file mode 100644 index 0000000..ee61333 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q16_s3_validate_pod.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# Create a test pod with the app-admin ServiceAccount +cat </dev/null || true + +# Check all permissions are correct +if [[ "$LIST_PODS" == "yes" && \ + "$GET_PODS" == "yes" && \ + "$WATCH_PODS" == "yes" && \ + "$LIST_DEPLOY" == "yes" && \ + "$GET_DEPLOY" == "yes" && \ + "$UPDATE_DEPLOY" == "yes" && \ + "$CREATE_CM" == "yes" && \ + "$DELETE_CM" == "yes" ]]; then + exit 0 +else + exit 1 +fi \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q17_s1_validate_deployments.sh b/facilitator/assets/exams/cka/002/scripts/validation/q17_s1_validate_deployments.sh new file mode 100644 index 0000000..25b3af1 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q17_s1_validate_deployments.sh @@ -0,0 +1,71 @@ +#!/bin/bash +set -e + +# Check web deployment +kubectl get deployment web -n network || { + echo "Deployment web not found in namespace network" + exit 1 +} + +# Check api deployment +kubectl get deployment api -n network || { + echo "Deployment api not found in namespace network" + exit 1 +} + +# Check db deployment +kubectl get deployment db -n network || { + echo "Deployment db not found in namespace network" + exit 1 +} + +# Check web deployment configuration +WEB_IMAGE=$(kubectl get deployment web -n network -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ ! "$WEB_IMAGE" =~ "nginx" ]]; then + echo "Web deployment is not using nginx image" + exit 1 +fi + +WEB_LABEL=$(kubectl get deployment web -n network -o jsonpath='{.spec.template.metadata.labels.app}') +if [[ "$WEB_LABEL" != "web" ]]; then + echo "Web deployment does not have correct label app=web" + exit 1 +fi + +# Check api deployment configuration +API_IMAGE=$(kubectl get deployment api -n network -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ ! "$API_IMAGE" =~ "nginx" ]]; then + echo "API deployment is not using nginx image" + exit 1 +fi + +API_LABEL=$(kubectl get deployment api -n network -o jsonpath='{.spec.template.metadata.labels.app}') +if [[ "$API_LABEL" != "api" ]]; then + echo "API deployment does not have correct label app=api" + exit 1 +fi + +# Check db deployment configuration +DB_IMAGE=$(kubectl get deployment db -n network -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ ! "$DB_IMAGE" =~ "postgres" ]]; then + echo "DB deployment is not using postgres image" + exit 1 +fi + +DB_LABEL=$(kubectl get deployment db -n network -o jsonpath='{.spec.template.metadata.labels.app}') +if [[ "$DB_LABEL" != "db" ]]; then + echo "DB deployment does not have correct label app=db" + exit 1 +fi + +# Check if all pods are running +for DEPLOY in web api db; do + READY_PODS=$(kubectl get deployment $DEPLOY -n network -o jsonpath='{.status.readyReplicas}') + if [[ -z "$READY_PODS" ]] || [[ "$READY_PODS" -lt 1 ]]; then + echo "Deployment $DEPLOY has no ready pods" + exit 1 + fi +done + +echo "Deployments validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q17_s2_validate_policies.sh b/facilitator/assets/exams/cka/002/scripts/validation/q17_s2_validate_policies.sh new file mode 100644 index 0000000..2e7c817 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q17_s2_validate_policies.sh @@ -0,0 +1,33 @@ +#!/bin/bash +set -e + +# Helper to check if a policy exists +check_policy_exists() { + local name=$1 + if ! kubectl get networkpolicy "$name" -n network >/dev/null 2>&1; then + echo "❌ $name not found" + exit 1 + fi +} + +# Helper to check if a policy egress allows traffic to a given app +check_egress_to() { + local policy=$1 + local app=$2 + local found=$(kubectl get networkpolicy "$policy" -n network -o jsonpath="{.spec.egress[*].to[*].podSelector.matchLabels.app}" | grep -w "$app" || true) + if [[ -z "$found" ]]; then + echo "❌ $policy does not allow egress to $app" + exit 1 + fi +} + +# Check required policies +check_policy_exists web-policy +check_policy_exists api-policy + +# Check egress rules +check_egress_to web-policy api +check_egress_to api-policy db + +echo "✅ NetworkPolicies validation successful" +exit 0 diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q17_s3_validate_enforcement.sh b/facilitator/assets/exams/cka/002/scripts/validation/q17_s3_validate_enforcement.sh new file mode 100644 index 0000000..ee12097 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q17_s3_validate_enforcement.sh @@ -0,0 +1,54 @@ +#!/bin/bash +set -e + +echo "🔎 Starting network policy validation..." + +NAMESPACE="network" +CLIENT_POD="test-client" +IMAGE="curlimages/curl:8.5.0" + +# 1. Ensure test-client exists +if ! kubectl get pod -n $NAMESPACE $CLIENT_POD &>/dev/null; then + echo "🚀 Creating test-client pod..." + kubectl run $CLIENT_POD -n $NAMESPACE --image=$IMAGE --restart=Never --command -- sleep 3600 + kubectl wait --for=condition=Ready pod/$CLIENT_POD -n $NAMESPACE --timeout=30s +else + echo "ℹ️ $CLIENT_POD pod already exists, skipping creation" +fi + +# 2. Test service connectivity using curl +echo "🔧 Testing service connectivity with curl..." + +test_connection() { + local target=$1 + local port=$2 + local expected=$3 + local url="http://$target:$port" + + echo "➡️ Testing: $CLIENT_POD ➡ $url (expected: $expected)" + + if kubectl exec -n $NAMESPACE $CLIENT_POD -- curl -s --max-time 2 $url &>/dev/null; then + if [[ "$expected" == "fail" ]]; then + echo "❌ FAILED: Connection to $url should be BLOCKED but SUCCEEDED" + exit 1 + else + echo "✅ Connection to $url succeeded" + fi + else + if [[ "$expected" == "success" ]]; then + echo "❌ FAILED: Connection to $url should be ALLOWED but FAILED" + exit 1 + else + echo "✅ Connection to $url correctly blocked" + fi + fi +} + +# Run tests +test_connection api 80 success +test_connection db 5432 success +test_connection web 80 fail +test_connection db 80 fail +test_connection api 5432 fail + +echo "🎉 All network policy tests passed successfully" diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q18_s1_validate_initial.sh b/facilitator/assets/exams/cka/002/scripts/validation/q18_s1_validate_initial.sh new file mode 100644 index 0000000..d22333e --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q18_s1_validate_initial.sh @@ -0,0 +1,53 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment app-v1 -n upgrade || { + echo "Deployment app-v1 not found in namespace upgrade" + exit 1 +} + +# Check replicas +REPLICAS=$(kubectl get deployment app-v1 -n upgrade -o jsonpath='{.spec.replicas}') +if [[ "$REPLICAS" != "4" ]]; then + echo "Incorrect number of replicas. Expected 4, got $REPLICAS" + exit 1 +fi + +# Check image +IMAGE=$(kubectl get deployment app-v1 -n upgrade -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ "$IMAGE" != "nginx:1.19" ]]; then + echo "Incorrect image. Expected nginx:1.19, got $IMAGE" + exit 1 +fi + +# Check if pods are running +READY_PODS=$(kubectl get deployment app-v1 -n upgrade -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "4" ]]; then + echo "Not all pods are ready. Expected 4, got $READY_PODS" + exit 1 +fi + +# Check deployment strategy +STRATEGY=$(kubectl get deployment app-v1 -n upgrade -o jsonpath='{.spec.strategy.type}') +if [[ "$STRATEGY" != "RollingUpdate" ]]; then + echo "Incorrect deployment strategy. Expected RollingUpdate, got $STRATEGY" + exit 1 +fi + +# Check max unavailable +MAX_UNAVAILABLE=$(kubectl get deployment app-v1 -n upgrade -o jsonpath='{.spec.strategy.rollingUpdate.maxUnavailable}') +if [[ "$MAX_UNAVAILABLE" != "1" ]]; then + echo "Incorrect maxUnavailable. Expected 1, got $MAX_UNAVAILABLE" + exit 1 +fi + +# Check max surge +MAX_SURGE=$(kubectl get deployment app-v1 -n upgrade -o jsonpath='{.spec.strategy.rollingUpdate.maxSurge}') +if [[ "$MAX_SURGE" != "1" ]]; then + echo "Incorrect maxSurge. Expected 1, got $MAX_SURGE" + exit 1 +fi + +echo "Initial deployment validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q18_s2_validate_update.sh b/facilitator/assets/exams/cka/002/scripts/validation/q18_s2_validate_update.sh new file mode 100644 index 0000000..4528e54 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q18_s2_validate_update.sh @@ -0,0 +1,56 @@ +#!/bin/bash +set -e + +NAMESPACE="upgrade" +DEPLOYMENT="app-v1" +HISTORY_FILE="/tmp/exam/rollout-history.txt" + +# Check if deployment exists +kubectl get deployment $DEPLOYMENT -n $NAMESPACE > /dev/null || { + echo "Deployment $DEPLOYMENT not found in namespace $NAMESPACE" + exit 1 +} + +# Check current image +CURRENT_IMAGE=$(kubectl get deployment $DEPLOYMENT -n $NAMESPACE -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ "$CURRENT_IMAGE" != "nginx:1.19" ]]; then + echo "Deployment is not using the expected image nginx:1.19. Got $CURRENT_IMAGE" + exit 1 +fi + +# Check both images exist in RS history +RS_IMAGES=$(kubectl get rs -n $NAMESPACE -l app=app-v1 -o jsonpath='{range .items[*]}{.spec.template.spec.containers[0].image}{"\n"}{end}') + +echo "$RS_IMAGES" | grep -q "nginx:1.19" || { + echo "ReplicaSet with image nginx:1.19 not found" + exit 1 +} + +echo "$RS_IMAGES" | grep -q "nginx:1.20" || { + echo "ReplicaSet with image nginx:1.20 not found" + exit 1 +} + +# Check rollout history file exists +if [[ ! -f "$HISTORY_FILE" ]]; then + echo "Rollout history file not found at $HISTORY_FILE" + exit 1 +fi + +# Check all pods are ready +READY_PODS=$(kubectl get deployment $DEPLOYMENT -n $NAMESPACE -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "4" ]]; then + echo "Expected 4 ready pods, but got $READY_PODS" + exit 1 +fi + +# Ensure no pods are running nginx:1.20 +PODS_WITH_20=$(kubectl get pods -n $NAMESPACE -l app=app-v1 -o jsonpath='{range .items[*]}{.metadata.name} {.spec.containers[0].image}{"\n"}{end}' | grep "nginx:1.20" || true) +if [[ -n "$PODS_WITH_20" ]]; then + echo "Some pods are still running nginx:1.20:" + echo "$PODS_WITH_20" + exit 1 +fi + +echo "✅ Validation successful: rollback confirmed, both images used, and all pods healthy" +exit 0 diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q18_s3_validate_rollback.sh b/facilitator/assets/exams/cka/002/scripts/validation/q18_s3_validate_rollback.sh new file mode 100644 index 0000000..d35d389 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q18_s3_validate_rollback.sh @@ -0,0 +1,50 @@ +#!/bin/bash +set -e + +NAMESPACE="upgrade" +DEPLOYMENT="app-v1" + +# Check if deployment exists +kubectl get deployment $DEPLOYMENT -n $NAMESPACE > /dev/null || { + echo "Deployment $DEPLOYMENT not found in namespace $NAMESPACE" + exit 1 +} + +# Check if current image is nginx:1.19 +CURRENT_IMAGE=$(kubectl get deployment $DEPLOYMENT -n $NAMESPACE -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ "$CURRENT_IMAGE" != "nginx:1.19" ]]; then + echo "Deployment not rolled back. Expected image nginx:1.19, got $CURRENT_IMAGE" + exit 1 +fi + +# Get list of ReplicaSets for this deployment +RS_LIST=$(kubectl get rs -n $NAMESPACE -l app=$DEPLOYMENT -o jsonpath='{range .items[*]}{.metadata.name} {.spec.template.spec.containers[0].image}{"\n"}{end}') + +# Check if both versions exist +echo "$RS_LIST" | grep -q "nginx:1.19" || { + echo "ReplicaSet with nginx:1.19 not found" + exit 1 +} + +echo "$RS_LIST" | grep -q "nginx:1.20" || { + echo "ReplicaSet with nginx:1.20 not found" + exit 1 +} + +# Check all pods are ready +READY_PODS=$(kubectl get deployment $DEPLOYMENT -n $NAMESPACE -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "4" ]]; then + echo "Not all pods are ready. Expected 4, got $READY_PODS" + exit 1 +fi + +# Check no pods are still using nginx:1.20 +BAD_PODS=$(kubectl get pods -n $NAMESPACE -l app=$DEPLOYMENT -o jsonpath='{range .items[*]}{.metadata.name} {.spec.containers[0].image}{"\n"}{end}' | grep "nginx:1.20" || true) +if [[ -n "$BAD_PODS" ]]; then + echo "Some pods are still using nginx:1.20:" + echo "$BAD_PODS" + exit 1 +fi + +echo "✅ Rollback validation successful" +exit 0 diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q19_s1_validate_priority.sh b/facilitator/assets/exams/cka/002/scripts/validation/q19_s1_validate_priority.sh new file mode 100644 index 0000000..43491ae --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q19_s1_validate_priority.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +# Check high priority class +kubectl get priorityclass high-priority || { + echo "PriorityClass high-priority not found" + exit 1 +} + +# Check low priority class +kubectl get priorityclass low-priority || { + echo "PriorityClass low-priority not found" + exit 1 +} + +# Check high priority value +HIGH_VALUE=$(kubectl get priorityclass high-priority -o jsonpath='{.value}') +if [[ "$HIGH_VALUE" != "1000" ]]; then + echo "Incorrect priority value for high-priority. Expected 1000, got $HIGH_VALUE" + exit 1 +fi + +# Check low priority value +LOW_VALUE=$(kubectl get priorityclass low-priority -o jsonpath='{.value}') +if [[ "$LOW_VALUE" != "100" ]]; then + echo "Incorrect priority value for low-priority. Expected 100, got $LOW_VALUE" + exit 1 +fi + +# Check that neither is set as default +HIGH_DEFAULT=$(kubectl get priorityclass high-priority -o jsonpath='{.globalDefault}') +LOW_DEFAULT=$(kubectl get priorityclass low-priority -o jsonpath='{.globalDefault}') + +if [[ "$HIGH_DEFAULT" == "true" ]] || [[ "$LOW_DEFAULT" == "true" ]]; then + echo "PriorityClasses should not be set as default" + exit 1 +fi + +echo "PriorityClass validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q19_s2_validate_pods.sh b/facilitator/assets/exams/cka/002/scripts/validation/q19_s2_validate_pods.sh new file mode 100644 index 0000000..22d2b6a --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q19_s2_validate_pods.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +# Check high priority pod +kubectl get pod high-priority -n scheduling || { + echo "Pod high-priority not found in namespace scheduling" + exit 1 +} + +# Check low priority pod +kubectl get pod low-priority -n scheduling || { + echo "Pod low-priority not found in namespace scheduling" + exit 1 +} + +# Check high priority pod configuration +HIGH_PRIORITY_CLASS=$(kubectl get pod high-priority -n scheduling -o jsonpath='{.spec.priorityClassName}') +if [[ "$HIGH_PRIORITY_CLASS" != "high-priority" ]]; then + echo "High priority pod not using correct PriorityClass" + exit 1 +fi + +# Check low priority pod configuration +LOW_PRIORITY_CLASS=$(kubectl get pod low-priority -n scheduling -o jsonpath='{.spec.priorityClassName}') +if [[ "$LOW_PRIORITY_CLASS" != "low-priority" ]]; then + echo "Low priority pod not using correct PriorityClass" + exit 1 +fi + +# Check if pods are running +for POD in high-priority low-priority; do + STATUS=$(kubectl get pod $POD -n scheduling -o jsonpath='{.status.phase}') + if [[ "$STATUS" != "Running" ]]; then + echo "Pod $POD is not running. Current status: $STATUS" + exit 1 + fi +done + +echo "Pods validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q19_s3_validate_antiaffinity.sh b/facilitator/assets/exams/cka/002/scripts/validation/q19_s3_validate_antiaffinity.sh new file mode 100644 index 0000000..a587e84 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q19_s3_validate_antiaffinity.sh @@ -0,0 +1,42 @@ +#!/bin/bash +set -e + +# Get pod nodes +HIGH_NODE=$(kubectl get pod high-priority -n scheduling -o jsonpath='{.spec.nodeName}') +LOW_NODE=$(kubectl get pod low-priority -n scheduling -o jsonpath='{.spec.nodeName}') + +# Check if pods are on different nodes +if [[ "$HIGH_NODE" == "$LOW_NODE" ]]; then + echo "Pods are scheduled on the same node: $HIGH_NODE" + exit 1 +fi + +# Check anti-affinity configuration for high priority pod +HIGH_AFFINITY=$(kubectl get pod high-priority -n scheduling -o json | jq -r '.spec.affinity.podAntiAffinity') +if [[ -z "$HIGH_AFFINITY" ]]; then + echo "High priority pod does not have pod anti-affinity configured" + exit 1 +fi + +# Check anti-affinity configuration for low priority pod +LOW_AFFINITY=$(kubectl get pod low-priority -n scheduling -o json | jq -r '.spec.affinity.podAntiAffinity') +if [[ -z "$LOW_AFFINITY" ]]; then + echo "Low priority pod does not have pod anti-affinity configured" + exit 1 +fi + +# Check if anti-affinity is using the correct topology key +HIGH_TOPOLOGY=$(kubectl get pod high-priority -n scheduling -o jsonpath='{.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].topologyKey}') +if [[ "$HIGH_TOPOLOGY" != "kubernetes.io/hostname" ]]; then + echo "High priority pod using incorrect topology key" + exit 1 +fi + +LOW_TOPOLOGY=$(kubectl get pod low-priority -n scheduling -o jsonpath='{.spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].topologyKey}') +if [[ "$LOW_TOPOLOGY" != "kubernetes.io/hostname" ]]; then + echo "Low priority pod using incorrect topology key" + exit 1 +fi + +echo "Anti-affinity validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q1_s1_validate_pvc.sh b/facilitator/assets/exams/cka/002/scripts/validation/q1_s1_validate_pvc.sh new file mode 100644 index 0000000..37a8d1f --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q1_s1_validate_pvc.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e + +# Check if PVC exists +kubectl get pvc data-pvc -n storage-task || { + echo "PVC data-pvc not found in namespace storage-task" + exit 1 +} + +# Validate storage class +STORAGE_CLASS=$(kubectl get pvc data-pvc -n storage-task -o jsonpath='{.spec.storageClassName}') +if [[ "$STORAGE_CLASS" != "standard" ]]; then + echo "Incorrect storage class. Expected 'standard', got '$STORAGE_CLASS'" + exit 1 +fi + +# Validate access mode +ACCESS_MODE=$(kubectl get pvc data-pvc -n storage-task -o jsonpath='{.spec.accessModes[0]}') +if [[ "$ACCESS_MODE" != "ReadWriteOnce" ]]; then + echo "Incorrect access mode. Expected 'ReadWriteOnce', got '$ACCESS_MODE'" + exit 1 +fi + +# Validate storage size +STORAGE_SIZE=$(kubectl get pvc data-pvc -n storage-task -o jsonpath='{.spec.resources.requests.storage}') +if [[ "$STORAGE_SIZE" != "2Gi" ]]; then + echo "Incorrect storage size. Expected '2Gi', got '$STORAGE_SIZE'" + exit 1 +fi + +echo "PVC validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q1_s2_validate_pod.sh b/facilitator/assets/exams/cka/002/scripts/validation/q1_s2_validate_pod.sh new file mode 100644 index 0000000..b0309f7 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q1_s2_validate_pod.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -e + +# Check if pod exists +kubectl get pod data-pod -n storage-task || { + echo "Pod data-pod not found in namespace storage-task" + exit 1 +} + +# Check if pod is running +POD_STATUS=$(kubectl get pod data-pod -n storage-task -o jsonpath='{.status.phase}') +if [[ "$POD_STATUS" != "Running" ]]; then + echo "Pod is not in Running state. Current state: $POD_STATUS" + exit 1 +fi + +# Check if pod is using nginx image +POD_IMAGE=$(kubectl get pod data-pod -n storage-task -o jsonpath='{.spec.containers[0].image}') +if [[ "$POD_IMAGE" != *"nginx"* ]]; then + echo "Pod is not using nginx image. Current image: $POD_IMAGE" + exit 1 +fi + +echo "Pod validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q1_s3_validate_mount.sh b/facilitator/assets/exams/cka/002/scripts/validation/q1_s3_validate_mount.sh new file mode 100644 index 0000000..4be520b --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q1_s3_validate_mount.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -e + +# Check if volume mount exists +MOUNT_PATH=$(kubectl get pod data-pod -n storage-task -o jsonpath='{.spec.containers[0].volumeMounts[?(@.name=="data")].mountPath}') +if [[ "$MOUNT_PATH" != "/usr/share/nginx/html" ]]; then + echo "Volume not mounted at correct path. Expected '/usr/share/nginx/html', got '$MOUNT_PATH'" + exit 1 +fi + +# Check if volume is using the PVC +VOLUME_PVC=$(kubectl get pod data-pod -n storage-task -o jsonpath='{.spec.volumes[?(@.name=="data")].persistentVolumeClaim.claimName}') +if [[ "$VOLUME_PVC" != "data-pvc" ]]; then + echo "Pod is not using the correct PVC. Expected 'data-pvc', got '$VOLUME_PVC'" + exit 1 +fi + +echo "Volume mount validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q20_s1_validate_port.sh b/facilitator/assets/exams/cka/002/scripts/validation/q20_s1_validate_port.sh new file mode 100644 index 0000000..770b46d --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q20_s1_validate_port.sh @@ -0,0 +1,28 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment failing-app -n troubleshoot || { + echo "Deployment failing-app not found in namespace troubleshoot" + exit 1 +} + +# Check container port configuration +PORT=$(kubectl get deployment failing-app -n troubleshoot -o jsonpath='{.spec.template.spec.containers[0].ports[0].containerPort}') +if [[ "$PORT" != "80" ]]; then + echo "Incorrect container port. Expected 80, got $PORT" + exit 1 +fi + +# Check if port is correctly configured in pods +PODS=$(kubectl get pods -n troubleshoot -l app=failing-app -o jsonpath='{.items[*].metadata.name}') +for POD in $PODS; do + POD_PORT=$(kubectl get pod $POD -n troubleshoot -o jsonpath='{.spec.containers[0].ports[0].containerPort}') + if [[ "$POD_PORT" != "80" ]]; then + echo "Pod $POD has incorrect port configuration. Expected 80, got $POD_PORT" + exit 1 + fi +done + +echo "Container port validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q20_s2_validate_memory.sh b/facilitator/assets/exams/cka/002/scripts/validation/q20_s2_validate_memory.sh new file mode 100644 index 0000000..bc8c99c --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q20_s2_validate_memory.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment failing-app -n troubleshoot || { + echo "Deployment failing-app not found in namespace troubleshoot" + exit 1 +} + +# Check memory limit configuration +MEM_LIMIT=$(kubectl get deployment failing-app -n troubleshoot -o jsonpath='{.spec.template.spec.containers[0].resources.limits.memory}') +if [[ "$MEM_LIMIT" != "256Mi" ]]; then + echo "Incorrect memory limit. Expected 256Mi, got $MEM_LIMIT" + exit 1 +fi + +# Check if memory limit is correctly applied to pods +PODS=$(kubectl get pods -n troubleshoot -l app=failing-app -o jsonpath='{.items[*].metadata.name}') +for POD in $PODS; do + POD_MEM_LIMIT=$(kubectl get pod $POD -n troubleshoot -o jsonpath='{.spec.containers[0].resources.limits.memory}') + if [[ "$POD_MEM_LIMIT" != "256Mi" ]]; then + echo "Pod $POD has incorrect memory limit. Expected 256Mi, got $POD_MEM_LIMIT" + exit 1 + fi +done + +# Check if pods are not being OOMKilled +for POD in $PODS; do + RESTARTS=$(kubectl get pod $POD -n troubleshoot -o jsonpath='{.status.containerStatuses[0].restartCount}') + LAST_STATE=$(kubectl get pod $POD -n troubleshoot -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}') + + if [[ "$LAST_STATE" == "OOMKilled" ]]; then + echo "Pod $POD was terminated due to OOMKilled" + exit 1 + fi +done + +echo "Memory limit validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q20_s3_validate_probe.sh b/facilitator/assets/exams/cka/002/scripts/validation/q20_s3_validate_probe.sh new file mode 100644 index 0000000..0ae62a9 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q20_s3_validate_probe.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment failing-app -n troubleshoot || { + echo "Deployment failing-app not found in namespace troubleshoot" + exit 1 +} + +# Check liveness probe configuration +PROBE_PORT=$(kubectl get deployment failing-app -n troubleshoot -o jsonpath='{.spec.template.spec.containers[0].livenessProbe.httpGet.port}') +if [[ "$PROBE_PORT" != "80" ]]; then + echo "Incorrect liveness probe port. Expected 80, got $PROBE_PORT" + exit 1 +fi + +# Check if probe is configured in pods +PODS=$(kubectl get pods -n troubleshoot -l app=failing-app -o jsonpath='{.items[*].metadata.name}') +for POD in $PODS; do + POD_PROBE_PORT=$(kubectl get pod $POD -n troubleshoot -o jsonpath='{.spec.containers[0].livenessProbe.httpGet.port}') + if [[ "$POD_PROBE_PORT" != "80" ]]; then + echo "Pod $POD has incorrect liveness probe port. Expected 80, got $POD_PROBE_PORT" + exit 1 + fi + + # Check if pod is being restarted due to failed liveness probe + RESTARTS=$(kubectl get pod $POD -n troubleshoot -o jsonpath='{.status.containerStatuses[0].restartCount}') + LAST_STATE=$(kubectl get pod $POD -n troubleshoot -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}') + + if [[ "$LAST_STATE" == "LivenessProbe" ]]; then + echo "Pod $POD is failing due to liveness probe" + exit 1 + fi +done + +echo "Liveness probe validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q20_s4_validate_pods.sh b/facilitator/assets/exams/cka/002/scripts/validation/q20_s4_validate_pods.sh new file mode 100644 index 0000000..97eaccf --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q20_s4_validate_pods.sh @@ -0,0 +1,61 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment failing-app -n troubleshoot || { + echo "Deployment failing-app not found in namespace troubleshoot" + exit 1 +} + +# Check if all pods are running +READY_PODS=$(kubectl get deployment failing-app -n troubleshoot -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "3" ]]; then + echo "Not all pods are ready. Expected 3, got $READY_PODS" + exit 1 +fi + +# Check pod status +PODS=$(kubectl get pods -n troubleshoot -l app=failing-app -o jsonpath='{.items[*].metadata.name}') +for POD in $PODS; do + # Check pod phase + PHASE=$(kubectl get pod $POD -n troubleshoot -o jsonpath='{.status.phase}') + if [[ "$PHASE" != "Running" ]]; then + echo "Pod $POD is not running. Current phase: $PHASE" + exit 1 + fi + + # Check container ready status + READY=$(kubectl get pod $POD -n troubleshoot -o jsonpath='{.status.containerStatuses[0].ready}') + if [[ "$READY" != "true" ]]; then + echo "Container in pod $POD is not ready" + exit 1 + fi + + # Check for recent restarts + RESTARTS=$(kubectl get pod $POD -n troubleshoot -o jsonpath='{.status.containerStatuses[0].restartCount}') + if [[ "$RESTARTS" -gt 0 ]]; then + # Check if the last restart was recent (within last minute) + LAST_RESTART=$(kubectl get pod $POD -n troubleshoot -o jsonpath='{.status.containerStatuses[0].lastState.terminated.finishedAt}') + if [[ -n "$LAST_RESTART" ]]; then + RESTART_TIME=$(date -d "$LAST_RESTART" +%s) + NOW=$(date +%s) + DIFF=$((NOW - RESTART_TIME)) + if [[ "$DIFF" -lt 60 ]]; then + echo "Pod $POD has recent restarts" + exit 1 + fi + fi + fi +done + +# Test pod functionality +for POD in $PODS; do + # Test nginx is serving on port 80 + kubectl exec $POD -n troubleshoot -- curl -s localhost:80 > /dev/null || { + echo "Pod $POD is not serving content on port 80" + exit 1 + } +done + +echo "Pod validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q2_s1_validate_sc.sh b/facilitator/assets/exams/cka/002/scripts/validation/q2_s1_validate_sc.sh new file mode 100644 index 0000000..3521faa --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q2_s1_validate_sc.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -e + +# Check if StorageClass exists +kubectl get storageclass fast-local || { + echo "StorageClass fast-local not found" + exit 1 +} + +# Validate provisioner +PROVISIONER=$(kubectl get storageclass fast-local -o jsonpath='{.provisioner}') +if [[ "$PROVISIONER" != "rancher.io/local-path" ]]; then + echo "Incorrect provisioner. Expected 'rancher.io/local-path', got '$PROVISIONER'" + exit 1 +fi + +# Validate volumeBindingMode +BINDING_MODE=$(kubectl get storageclass fast-local -o jsonpath='{.volumeBindingMode}') +if [[ "$BINDING_MODE" != "WaitForFirstConsumer" ]]; then + echo "Incorrect volumeBindingMode. Expected 'WaitForFirstConsumer', got '$BINDING_MODE'" + exit 1 +fi + +echo "StorageClass validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q2_s2_validate_default.sh b/facilitator/assets/exams/cka/002/scripts/validation/q2_s2_validate_default.sh new file mode 100644 index 0000000..fd09d82 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q2_s2_validate_default.sh @@ -0,0 +1,12 @@ +#!/bin/bash +set -e + +# Check if StorageClass is marked as default +IS_DEFAULT=$(kubectl get storageclass fast-local -o jsonpath='{.metadata.annotations.storageclass\.kubernetes\.io/is-default-class}') +if [[ "$IS_DEFAULT" != "true" ]]; then + echo "StorageClass fast-local is not marked as default" + exit 1 +fi + +echo "Default StorageClass validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q2_s3_validate_no_other_default.sh b/facilitator/assets/exams/cka/002/scripts/validation/q2_s3_validate_no_other_default.sh new file mode 100644 index 0000000..7d41ccf --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q2_s3_validate_no_other_default.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e + +# Count number of default StorageClasses +DEFAULT_COUNT=$(kubectl get storageclass -o jsonpath='{.items[?(@.metadata.annotations.storageclass\.kubernetes\.io/is-default-class=="true")].metadata.name}' | wc -w) + +if [[ "$DEFAULT_COUNT" -ne 1 ]]; then + echo "Found $DEFAULT_COUNT default StorageClasses. Expected exactly 1" + exit 1 +fi + +# Verify the only default is our StorageClass +DEFAULT_SC=$(kubectl get storageclass -o jsonpath='{.items[?(@.metadata.annotations.storageclass\.kubernetes\.io/is-default-class=="true")].metadata.name}') +if [[ "$DEFAULT_SC" != "fast-local" ]]; then + echo "Wrong StorageClass is default. Expected 'fast-local', got '$DEFAULT_SC'" + exit 1 +fi + +echo "No other default StorageClass validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q3_s1_validate_pv.sh b/facilitator/assets/exams/cka/002/scripts/validation/q3_s1_validate_pv.sh new file mode 100644 index 0000000..b2702e4 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q3_s1_validate_pv.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +# Check if PV exists +kubectl get pv manual-pv || { + echo "PV manual-pv not found" + exit 1 +} + +# Validate storage size +STORAGE_SIZE=$(kubectl get pv manual-pv -o jsonpath='{.spec.capacity.storage}') +if [[ "$STORAGE_SIZE" != "1Gi" ]]; then + echo "Incorrect storage size. Expected '1Gi', got '$STORAGE_SIZE'" + exit 1 +fi + +# Validate access mode +ACCESS_MODE=$(kubectl get pv manual-pv -o jsonpath='{.spec.accessModes[0]}') +if [[ "$ACCESS_MODE" != "ReadWriteOnce" ]]; then + echo "Incorrect access mode. Expected 'ReadWriteOnce', got '$ACCESS_MODE'" + exit 1 +fi + +# Validate host path +HOST_PATH=$(kubectl get pv manual-pv -o jsonpath='{.spec.hostPath.path}') +if [[ "$HOST_PATH" != "/mnt/data" ]]; then + echo "Incorrect host path. Expected '/mnt/data', got '$HOST_PATH'" + exit 1 +fi + +# Validate node affinity +NODE_HOSTNAME=$(kubectl get pv manual-pv -o jsonpath='{.spec.nodeAffinity.required.nodeSelectorTerms[0].matchExpressions[?(@.key=="kubernetes.io/hostname")].values[0]}') +if [[ "$NODE_HOSTNAME" != "k3d-cluster-agent-0" ]]; then + echo "Incorrect node affinity. Expected 'k3d-cluster-agent-0', got '$NODE_HOSTNAME'" + exit 1 +fi + +echo "PV validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q3_s2_validate_pvc.sh b/facilitator/assets/exams/cka/002/scripts/validation/q3_s2_validate_pvc.sh new file mode 100644 index 0000000..5ed729a --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q3_s2_validate_pvc.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +# Check if PVC exists +kubectl get pvc manual-pvc -n manual-storage || { + echo "PVC manual-pvc not found in namespace manual-storage" + exit 1 +} + +# Validate storage size +STORAGE_SIZE=$(kubectl get pvc manual-pvc -n manual-storage -o jsonpath='{.spec.resources.requests.storage}') +if [[ "$STORAGE_SIZE" != "1Gi" ]]; then + echo "Incorrect storage size. Expected '1Gi', got '$STORAGE_SIZE'" + exit 1 +fi + +# Validate access mode +ACCESS_MODE=$(kubectl get pvc manual-pvc -n manual-storage -o jsonpath='{.spec.accessModes[0]}') +if [[ "$ACCESS_MODE" != "ReadWriteOnce" ]]; then + echo "Incorrect access mode. Expected 'ReadWriteOnce', got '$ACCESS_MODE'" + exit 1 +fi + +# Check if PVC is bound +PVC_STATUS=$(kubectl get pvc manual-pvc -n manual-storage -o jsonpath='{.status.phase}') +if [[ "$PVC_STATUS" != "Bound" ]]; then + echo "PVC is not bound. Current status: $PVC_STATUS" + exit 1 +fi + +# Verify it's bound to the correct PV +BOUND_PV=$(kubectl get pvc manual-pvc -n manual-storage -o jsonpath='{.spec.volumeName}') +if [[ "$BOUND_PV" != "manual-pv" ]]; then + echo "PVC is bound to wrong PV. Expected 'manual-pv', got '$BOUND_PV'" + exit 1 +fi + +echo "PVC validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q3_s3_validate_pod.sh b/facilitator/assets/exams/cka/002/scripts/validation/q3_s3_validate_pod.sh new file mode 100644 index 0000000..3db9064 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q3_s3_validate_pod.sh @@ -0,0 +1,52 @@ +#!/bin/bash +set -e + +# Check if pod exists +kubectl get pod manual-pod -n manual-storage || { + echo "Pod manual-pod not found in namespace manual-storage" + exit 1 +} + +# Check if pod is running +POD_STATUS=$(kubectl get pod manual-pod -n manual-storage -o jsonpath='{.status.phase}') +if [[ "$POD_STATUS" != "Running" ]]; then + echo "Pod is not in Running state. Current state: $POD_STATUS" + exit 1 +fi + +# Check if pod is using busybox image +POD_IMAGE=$(kubectl get pod manual-pod -n manual-storage -o jsonpath='{.spec.containers[0].image}') +if [[ "$POD_IMAGE" != *"busybox"* ]]; then + echo "Pod is not using busybox image. Current image: $POD_IMAGE" + exit 1 +fi + +# Check if volume is mounted correctly +MOUNT_PATH=$(kubectl get pod manual-pod -n manual-storage -o jsonpath='{.spec.containers[0].volumeMounts[0].mountPath}') +if [[ "$MOUNT_PATH" != "/data" ]]; then + echo "Volume not mounted at correct path. Expected '/data', got '$MOUNT_PATH'" + exit 1 +fi + +# Check if the correct PVC is used +VOLUME_PVC=$(kubectl get pod manual-pod -n manual-storage -o jsonpath='{.spec.volumes[0].persistentVolumeClaim.claimName}') +if [[ "$VOLUME_PVC" != "manual-pvc" ]]; then + echo "Pod is not using the correct PVC. Expected 'manual-pvc', got '$VOLUME_PVC'" + exit 1 +fi + +# Check if command is set correctly +POD_COMMAND=$(kubectl get pod manual-pod -n manual-storage -o jsonpath='{.spec.containers[0].command[0]}') +if [[ "$POD_COMMAND" != "sleep" ]]; then + echo "Pod command is not set to sleep" + exit 1 +fi + +POD_ARGS=$(kubectl get pod manual-pod -n manual-storage -o jsonpath='{.spec.containers[0].command[1]}') +if [[ "$POD_ARGS" != "3600" ]]; then + echo "Pod sleep duration is not set to 3600" + exit 1 +fi + +echo "Pod validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q4_s1_validate_deployment.sh b/facilitator/assets/exams/cka/002/scripts/validation/q4_s1_validate_deployment.sh new file mode 100644 index 0000000..44b9e2b --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q4_s1_validate_deployment.sh @@ -0,0 +1,51 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment scaling-app -n scaling || { + echo "Deployment scaling-app not found in namespace scaling" + exit 1 +} + +# Check replicas +REPLICAS=$(kubectl get deployment scaling-app -n scaling -o jsonpath='{.spec.replicas}') +if [[ "$REPLICAS" != "2" ]]; then + echo "Incorrect number of replicas. Expected 2, got $REPLICAS" + exit 1 +fi + +# Check image +IMAGE=$(kubectl get deployment scaling-app -n scaling -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ "$IMAGE" != *"nginx"* ]]; then + echo "Incorrect image. Expected nginx, got $IMAGE" + exit 1 +fi + +# Check resource requests +CPU_REQUEST=$(kubectl get deployment scaling-app -n scaling -o jsonpath='{.spec.template.spec.containers[0].resources.requests.cpu}') +if [[ "$CPU_REQUEST" != "200m" ]]; then + echo "Incorrect CPU request. Expected 200m, got $CPU_REQUEST" + exit 1 +fi + +MEM_REQUEST=$(kubectl get deployment scaling-app -n scaling -o jsonpath='{.spec.template.spec.containers[0].resources.requests.memory}') +if [[ "$MEM_REQUEST" != "256Mi" ]]; then + echo "Incorrect memory request. Expected 256Mi, got $MEM_REQUEST" + exit 1 +fi + +# Check resource limits +CPU_LIMIT=$(kubectl get deployment scaling-app -n scaling -o jsonpath='{.spec.template.spec.containers[0].resources.limits.cpu}') +if [[ "$CPU_LIMIT" != "500m" ]]; then + echo "Incorrect CPU limit. Expected 500m, got $CPU_LIMIT" + exit 1 +fi + +MEM_LIMIT=$(kubectl get deployment scaling-app -n scaling -o jsonpath='{.spec.template.spec.containers[0].resources.limits.memory}') +if [[ "$MEM_LIMIT" != "512Mi" ]]; then + echo "Incorrect memory limit. Expected 512Mi, got $MEM_LIMIT" + exit 1 +fi + +echo "Deployment validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q4_s2_validate_hpa.sh b/facilitator/assets/exams/cka/002/scripts/validation/q4_s2_validate_hpa.sh new file mode 100644 index 0000000..7bd60ec --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q4_s2_validate_hpa.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +# Check if HPA exists +kubectl get hpa -n scaling scaling-app || { + echo "HorizontalPodAutoscaler scaling-app not found in namespace scaling" + exit 1 +} + +# Check min replicas +MIN_REPLICAS=$(kubectl get hpa -n scaling scaling-app -o jsonpath='{.spec.minReplicas}') +if [[ "$MIN_REPLICAS" != "2" ]]; then + echo "Incorrect minimum replicas. Expected 2, got $MIN_REPLICAS" + exit 1 +fi + +# Check max replicas +MAX_REPLICAS=$(kubectl get hpa -n scaling scaling-app -o jsonpath='{.spec.maxReplicas}') +if [[ "$MAX_REPLICAS" != "5" ]]; then + echo "Incorrect maximum replicas. Expected 5, got $MAX_REPLICAS" + exit 1 +fi + +# Check target CPU utilization +TARGET_CPU=$(kubectl get hpa -n scaling scaling-app -o jsonpath='{.spec.metrics[0].resource.target.averageUtilization}') +if [[ "$TARGET_CPU" != "70" ]]; then + echo "Incorrect target CPU utilization. Expected 70, got $TARGET_CPU" + exit 1 +fi + +# Check if HPA is targeting the correct deployment +TARGET_REF=$(kubectl get hpa -n scaling scaling-app -o jsonpath='{.spec.scaleTargetRef.name}') +if [[ "$TARGET_REF" != "scaling-app" ]]; then + echo "HPA is not targeting the correct deployment. Expected scaling-app, got $TARGET_REF" + exit 1 +fi + +echo "HPA validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q4_s3_validate_resources.sh b/facilitator/assets/exams/cka/002/scripts/validation/q4_s3_validate_resources.sh new file mode 100644 index 0000000..3be802c --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q4_s3_validate_resources.sh @@ -0,0 +1,46 @@ +#!/bin/bash +set -e + +# Check if pods are running +READY_PODS=$(kubectl get deployment scaling-app -n scaling -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "2" ]]; then + echo "Not all pods are ready. Expected 2, got $READY_PODS" + exit 1 +fi + +# Get pod names +PODS=$(kubectl get pods -n scaling -l app=scaling-app -o jsonpath='{.items[*].metadata.name}') + +# Check each pod's resource configuration +for POD in $PODS; do + # Check CPU request + CPU_REQUEST=$(kubectl get pod $POD -n scaling -o jsonpath='{.spec.containers[0].resources.requests.cpu}') + if [[ "$CPU_REQUEST" != "200m" ]]; then + echo "Pod $POD has incorrect CPU request. Expected 200m, got $CPU_REQUEST" + exit 1 + fi + + # Check memory request + MEM_REQUEST=$(kubectl get pod $POD -n scaling -o jsonpath='{.spec.containers[0].resources.requests.memory}') + if [[ "$MEM_REQUEST" != "256Mi" ]]; then + echo "Pod $POD has incorrect memory request. Expected 256Mi, got $MEM_REQUEST" + exit 1 + fi + + # Check CPU limit + CPU_LIMIT=$(kubectl get pod $POD -n scaling -o jsonpath='{.spec.containers[0].resources.limits.cpu}') + if [[ "$CPU_LIMIT" != "500m" ]]; then + echo "Pod $POD has incorrect CPU limit. Expected 500m, got $CPU_LIMIT" + exit 1 + fi + + # Check memory limit + MEM_LIMIT=$(kubectl get pod $POD -n scaling -o jsonpath='{.spec.containers[0].resources.limits.memory}') + if [[ "$MEM_LIMIT" != "512Mi" ]]; then + echo "Pod $POD has incorrect memory limit. Expected 512Mi, got $MEM_LIMIT" + exit 1 + fi +done + +echo "Resource configuration validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q5_s1_validate_node_label.sh b/facilitator/assets/exams/cka/002/scripts/validation/q5_s1_validate_node_label.sh new file mode 100644 index 0000000..f156705 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q5_s1_validate_node_label.sh @@ -0,0 +1,18 @@ +#!/bin/bash +set -e + +# Check if node exists +kubectl get node k3d-cluster-agent-1 || { + echo "Node k3d-cluster-agent-1 not found" + exit 1 +} + +# Check if node has the required label +LABEL_VALUE=$(kubectl get node k3d-cluster-agent-1 -o jsonpath='{.metadata.labels.disk}') +if [[ "$LABEL_VALUE" != "ssd" ]]; then + echo "Node k3d-cluster-agent-1 does not have the correct label. Expected disk=ssd, got disk=$LABEL_VALUE" + exit 1 +fi + +echo "Node label validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q5_s2_validate_affinity.sh b/facilitator/assets/exams/cka/002/scripts/validation/q5_s2_validate_affinity.sh new file mode 100644 index 0000000..e6390c6 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q5_s2_validate_affinity.sh @@ -0,0 +1,41 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment app-scheduling -n scheduling || { + echo "Deployment app-scheduling not found in namespace scheduling" + exit 1 +} + +# Check replicas +REPLICAS=$(kubectl get deployment app-scheduling -n scheduling -o jsonpath='{.spec.replicas}') +if [[ "$REPLICAS" != "3" ]]; then + echo "Incorrect number of replicas. Expected 3, got $REPLICAS" + exit 1 +fi + +# Check if using node affinity (not node selector) +NODE_SELECTOR=$(kubectl get deployment app-scheduling -n scheduling -o jsonpath='{.spec.template.spec.nodeSelector}') +if [[ -n "$NODE_SELECTOR" ]]; then + echo "Deployment is using nodeSelector instead of nodeAffinity" + exit 1 +fi + +# Check node affinity configuration +AFFINITY_TYPE=$(kubectl get deployment app-scheduling -n scheduling -o jsonpath='{.spec.template.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution}') +if [[ -z "$AFFINITY_TYPE" ]]; then + echo "Deployment is not using requiredDuringSchedulingIgnoredDuringExecution" + exit 1 +fi + +# Check affinity rule +AFFINITY_KEY=$(kubectl get deployment app-scheduling -n scheduling -o jsonpath='{.spec.template.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].key}') +AFFINITY_VALUE=$(kubectl get deployment app-scheduling -n scheduling -o jsonpath='{.spec.template.spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[0].values[0]}') + +if [[ "$AFFINITY_KEY" != "disk" ]] || [[ "$AFFINITY_VALUE" != "ssd" ]]; then + echo "Incorrect node affinity configuration. Expected disk=ssd" + exit 1 +fi + +echo "Node affinity validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q5_s3_validate_pod_placement.sh b/facilitator/assets/exams/cka/002/scripts/validation/q5_s3_validate_pod_placement.sh new file mode 100644 index 0000000..dd75205 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q5_s3_validate_pod_placement.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e + +# Check if all pods are running +READY_PODS=$(kubectl get deployment app-scheduling -n scheduling -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "3" ]]; then + echo "Not all pods are ready. Expected 3, got $READY_PODS" + exit 1 +fi + +# Get all pods +PODS=$(kubectl get pods -n scheduling -l app=app-scheduling -o jsonpath='{.items[*].metadata.name}') + +# Check each pod's node placement +for POD in $PODS; do + NODE=$(kubectl get pod $POD -n scheduling -o jsonpath='{.spec.nodeName}') + if [[ "$NODE" != "k3d-cluster-agent-1" ]]; then + echo "Pod $POD is running on wrong node. Expected k3d-cluster-agent-1, got $NODE" + exit 1 + fi +done + +echo "Pod placement validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q6_s1_validate_psp.sh b/facilitator/assets/exams/cka/002/scripts/validation/q6_s1_validate_psp.sh new file mode 100644 index 0000000..66b0cf0 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q6_s1_validate_psp.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -e + +NAMESPACE="security" + +# Check if namespace exists +kubectl get namespace "$NAMESPACE" > /dev/null || { + echo "Namespace '$NAMESPACE' not found" + exit 1 +} + +# Check enforce label +ENFORCE=$(kubectl get namespace "$NAMESPACE" -o jsonpath='{.metadata.labels.pod-security\.kubernetes\.io/enforce}') +if [[ "$ENFORCE" != "restricted" ]]; then + echo "Namespace '$NAMESPACE' does not have enforce=restricted label" + exit 1 +fi + +# Check enforce-version label +VERSION=$(kubectl get namespace "$NAMESPACE" -o jsonpath='{.metadata.labels.pod-security\.kubernetes\.io/enforce-version}') +if [[ "$VERSION" != "latest" ]]; then + echo "Namespace '$NAMESPACE' does not have enforce-version=latest label" + exit 1 +fi + +echo "PSA label validation successful" +exit 0 diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q6_s2_validate_pod.sh b/facilitator/assets/exams/cka/002/scripts/validation/q6_s2_validate_pod.sh new file mode 100644 index 0000000..b7349c6 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q6_s2_validate_pod.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -e + +# Check if pod exists +kubectl get pod secure-pod -n security || { + echo "Pod secure-pod not found in namespace security" + exit 1 +} + +# Check if pod is running +POD_STATUS=$(kubectl get pod secure-pod -n security -o jsonpath='{.status.phase}') +if [[ "$POD_STATUS" != "Running" ]]; then + echo "Pod is not in Running state. Current state: $POD_STATUS" + exit 1 +fi + +# Check if pod is using nginx image +POD_IMAGE=$(kubectl get pod secure-pod -n security -o jsonpath='{.spec.containers[0].image}') +if [[ "$POD_IMAGE" != *"nginx"* ]]; then + echo "Pod is not using nginx image. Current image: $POD_IMAGE" + exit 1 +fi + +# Check if pod is running as non-root +RUN_AS_USER=$(kubectl get pod secure-pod -n security -o jsonpath='{.spec.securityContext.runAsNonRoot}') +if [[ "$RUN_AS_USER" != "true" ]]; then + echo "Pod is not configured to run as non-root user" + exit 1 +fi + +# Check if pod has no privileged containers +PRIVILEGED=$(kubectl get pod secure-pod -n security -o jsonpath='{.spec.containers[0].securityContext.privileged}') +if [[ "$PRIVILEGED" == "true" ]]; then + echo "Pod has privileged container" + exit 1 +fi + +echo "Pod validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q7_s1_validate_taint.sh b/facilitator/assets/exams/cka/002/scripts/validation/q7_s1_validate_taint.sh new file mode 100644 index 0000000..56d6b6d --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q7_s1_validate_taint.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e + +# Check if node exists +kubectl get node k3d-cluster-agent-1 || { + echo "Node k3d-cluster-agent-1 not found" + exit 1 +} + +# Check if taint exists +TAINT=$(kubectl get node k3d-cluster-agent-1 -o jsonpath='{.spec.taints[?(@.key=="special-workload")]}') +if [[ -z "$TAINT" ]]; then + echo "Taint special-workload not found on node k3d-cluster-agent-1" + exit 1 +fi + +# Check taint value +TAINT_VALUE=$(kubectl get node k3d-cluster-agent-1 -o jsonpath='{.spec.taints[?(@.key=="special-workload")].value}') +if [[ "$TAINT_VALUE" != "true" ]]; then + echo "Incorrect taint value. Expected 'true', got '$TAINT_VALUE'" + exit 1 +fi + +# Check taint effect +TAINT_EFFECT=$(kubectl get node k3d-cluster-agent-1 -o jsonpath='{.spec.taints[?(@.key=="special-workload")].effect}') +if [[ "$TAINT_EFFECT" != "NoSchedule" ]]; then + echo "Incorrect taint effect. Expected 'NoSchedule', got '$TAINT_EFFECT'" + exit 1 +fi + +echo "Node taint validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q7_s2_validate_toleration_deploy.sh b/facilitator/assets/exams/cka/002/scripts/validation/q7_s2_validate_toleration_deploy.sh new file mode 100644 index 0000000..1175eee --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q7_s2_validate_toleration_deploy.sh @@ -0,0 +1,51 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment toleration-deploy -n scheduling || { + echo "Deployment toleration-deploy not found in namespace scheduling" + exit 1 +} + +# Check replicas +REPLICAS=$(kubectl get deployment toleration-deploy -n scheduling -o jsonpath='{.spec.replicas}') +if [[ "$REPLICAS" != "2" ]]; then + echo "Incorrect number of replicas. Expected 2, got $REPLICAS" + exit 1 +fi + +# Check if using nginx image +IMAGE=$(kubectl get deployment toleration-deploy -n scheduling -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ "$IMAGE" != *"nginx"* ]]; then + echo "Deployment is not using nginx image. Current image: $IMAGE" + exit 1 +fi + +# Check toleration configuration +TOLERATION_KEY=$(kubectl get deployment toleration-deploy -n scheduling -o jsonpath='{.spec.template.spec.tolerations[?(@.key=="special-workload")].key}') +if [[ "$TOLERATION_KEY" != "special-workload" ]]; then + echo "Toleration key not found or incorrect" + exit 1 +fi + +TOLERATION_VALUE=$(kubectl get deployment toleration-deploy -n scheduling -o jsonpath='{.spec.template.spec.tolerations[?(@.key=="special-workload")].value}') +if [[ "$TOLERATION_VALUE" != "true" ]]; then + echo "Incorrect toleration value. Expected 'true', got '$TOLERATION_VALUE'" + exit 1 +fi + +TOLERATION_EFFECT=$(kubectl get deployment toleration-deploy -n scheduling -o jsonpath='{.spec.template.spec.tolerations[?(@.key=="special-workload")].effect}') +if [[ "$TOLERATION_EFFECT" != "NoSchedule" ]]; then + echo "Incorrect toleration effect. Expected 'NoSchedule', got '$TOLERATION_EFFECT'" + exit 1 +fi + +# Check if pods are running +READY_PODS=$(kubectl get deployment toleration-deploy -n scheduling -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "2" ]]; then + echo "Not all pods are ready. Expected 2, got $READY_PODS" + exit 1 +fi + +echo "Toleration deployment validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q7_s3_validate_normal_deploy.sh b/facilitator/assets/exams/cka/002/scripts/validation/q7_s3_validate_normal_deploy.sh new file mode 100644 index 0000000..082fa71 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q7_s3_validate_normal_deploy.sh @@ -0,0 +1,49 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment normal-deploy -n scheduling || { + echo "Deployment normal-deploy not found in namespace scheduling" + exit 1 +} + +# Check replicas +REPLICAS=$(kubectl get deployment normal-deploy -n scheduling -o jsonpath='{.spec.replicas}') +if [[ "$REPLICAS" != "2" ]]; then + echo "Incorrect number of replicas. Expected 2, got $REPLICAS" + exit 1 +fi + +# Check if using nginx image +IMAGE=$(kubectl get deployment normal-deploy -n scheduling -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ "$IMAGE" != *"nginx"* ]]; then + echo "Deployment is not using nginx image. Current image: $IMAGE" + exit 1 +fi + +# Check that there are no tolerations for special-workload +TOLERATIONS=$(kubectl get deployment normal-deploy -n scheduling -o jsonpath='{.spec.template.spec.tolerations[?(@.key=="special-workload")]}') +if [[ -n "$TOLERATIONS" ]]; then + echo "Deployment should not have tolerations for special-workload" + exit 1 +fi + +# Check if pods are running +READY_PODS=$(kubectl get deployment normal-deploy -n scheduling -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "2" ]]; then + echo "Not all pods are ready. Expected 2, got $READY_PODS" + exit 1 +fi + +# Verify pods are not on tainted node +PODS=$(kubectl get pods -n scheduling -l app=normal-deploy -o jsonpath='{.items[*].metadata.name}') +for POD in $PODS; do + NODE=$(kubectl get pod $POD -n scheduling -o jsonpath='{.spec.nodeName}') + if [[ "$NODE" == "k3d-cluster-agent-1" ]]; then + echo "Pod $POD is scheduled on tainted node k3d-cluster-agent-1" + exit 1 + fi +done + +echo "Normal deployment validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q8_s1_validate_statefulset.sh b/facilitator/assets/exams/cka/002/scripts/validation/q8_s1_validate_statefulset.sh new file mode 100644 index 0000000..e71988b --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q8_s1_validate_statefulset.sh @@ -0,0 +1,52 @@ +#!/bin/bash +set -e + +# Check if StatefulSet exists +kubectl get statefulset web -n stateful || { + echo "StatefulSet web not found in namespace stateful" + exit 1 +} + +# Check replicas +REPLICAS=$(kubectl get statefulset web -n stateful -o jsonpath='{.spec.replicas}') +if [[ "$REPLICAS" != "3" ]]; then + echo "Incorrect number of replicas. Expected 3, got $REPLICAS" + exit 1 +fi + +# Check image +IMAGE=$(kubectl get statefulset web -n stateful -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ "$IMAGE" != *"nginx"* ]]; then + echo "StatefulSet is not using nginx image. Current image: $IMAGE" + exit 1 +fi + +# Check volume mount +MOUNT_PATH=$(kubectl get statefulset web -n stateful -o jsonpath='{.spec.template.spec.containers[0].volumeMounts[0].mountPath}') +if [[ "$MOUNT_PATH" != "/usr/share/nginx/html" ]]; then + echo "Incorrect volume mount path. Expected /usr/share/nginx/html, got $MOUNT_PATH" + exit 1 +fi + +# Check volume claim template +STORAGE_CLASS=$(kubectl get statefulset web -n stateful -o jsonpath='{.spec.volumeClaimTemplates[0].spec.storageClassName}') +if [[ "$STORAGE_CLASS" != "cold" ]]; then + echo "Incorrect storage class. Expected cold, got $STORAGE_CLASS" + exit 1 +fi + +STORAGE_SIZE=$(kubectl get statefulset web -n stateful -o jsonpath='{.spec.volumeClaimTemplates[0].spec.resources.requests.storage}') +if [[ "$STORAGE_SIZE" != "1Gi" ]]; then + echo "Incorrect storage size. Expected 1Gi, got $STORAGE_SIZE" + exit 1 +fi + +# Check if pods are running +READY_PODS=$(kubectl get statefulset web -n stateful -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "3" ]]; then + echo "Not all pods are ready. Expected 3, got $READY_PODS" + exit 1 +fi + +echo "StatefulSet validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q8_s2_validate_service.sh b/facilitator/assets/exams/cka/002/scripts/validation/q8_s2_validate_service.sh new file mode 100644 index 0000000..7da4c4d --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q8_s2_validate_service.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +# Check if service exists +kubectl get service web-svc -n stateful || { + echo "Service web-svc not found in namespace stateful" + exit 1 +} + +# Check if it's a headless service +CLUSTER_IP=$(kubectl get service web-svc -n stateful -o jsonpath='{.spec.clusterIP}') +if [[ "$CLUSTER_IP" != "None" ]]; then + echo "Service is not headless. ClusterIP should be None, got $CLUSTER_IP" + exit 1 +fi + +# Check selector +SELECTOR=$(kubectl get service web-svc -n stateful -o jsonpath='{.spec.selector.app}') +if [[ "$SELECTOR" != "web" ]]; then + echo "Incorrect service selector. Expected app=web, got app=$SELECTOR" + exit 1 +fi + +# Check ports +PORT=$(kubectl get service web-svc -n stateful -o jsonpath='{.spec.ports[0].port}') +if [[ "$PORT" != "80" ]]; then + echo "Incorrect port. Expected 80, got $PORT" + exit 1 +fi + +# # Verify DNS records exist for pods +# for i in {0..2}; do +# nslookup web-$i.web-svc.stateful.svc.cluster.local 2>/dev/null || { +# echo "DNS record for web-$i.web-svc.stateful.svc.cluster.local not found" +# exit 1 +# } +# done + +echo "Headless service validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q8_s3_validate_storage.sh b/facilitator/assets/exams/cka/002/scripts/validation/q8_s3_validate_storage.sh new file mode 100644 index 0000000..7ba3d48 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q8_s3_validate_storage.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -e + +# Check if PVCs are created +for i in {0..2}; do + PVC_NAME="www-web-$i" + + # Check if PVC exists + kubectl get pvc $PVC_NAME -n stateful || { + echo "PVC $PVC_NAME not found in namespace stateful" + exit 1 + } + + # Check storage class + STORAGE_CLASS=$(kubectl get pvc $PVC_NAME -n stateful -o jsonpath='{.spec.storageClassName}') + if [[ "$STORAGE_CLASS" != "cold" ]]; then + echo "PVC $PVC_NAME has incorrect storage class. Expected cold, got $STORAGE_CLASS" + exit 1 + fi + + # Check storage size + STORAGE_SIZE=$(kubectl get pvc $PVC_NAME -n stateful -o jsonpath='{.spec.resources.requests.storage}') + if [[ "$STORAGE_SIZE" != "1Gi" ]]; then + echo "PVC $PVC_NAME has incorrect storage size. Expected 1Gi, got $STORAGE_SIZE" + exit 1 + fi + + # Check if PVC is bound + STATUS=$(kubectl get pvc $PVC_NAME -n stateful -o jsonpath='{.status.phase}') + if [[ "$STATUS" != "Bound" ]]; then + echo "PVC $PVC_NAME is not bound. Current status: $STATUS" + exit 1 + fi +done + +echo "Storage validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q9_s1_validate_deployment_svc.sh b/facilitator/assets/exams/cka/002/scripts/validation/q9_s1_validate_deployment_svc.sh new file mode 100644 index 0000000..eb800ee --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q9_s1_validate_deployment_svc.sh @@ -0,0 +1,52 @@ +#!/bin/bash +set -e + +# Check if deployment exists +kubectl get deployment web-app -n dns-debug || { + echo "Deployment web-app not found in namespace dns-debug" + exit 1 +} + +# Check replicas +REPLICAS=$(kubectl get deployment web-app -n dns-debug -o jsonpath='{.spec.replicas}') +if [[ "$REPLICAS" != "3" ]]; then + echo "Incorrect number of replicas. Expected 3, got $REPLICAS" + exit 1 +fi + +# Check image +IMAGE=$(kubectl get deployment web-app -n dns-debug -o jsonpath='{.spec.template.spec.containers[0].image}') +if [[ "$IMAGE" != *"nginx"* ]]; then + echo "Deployment is not using nginx image. Current image: $IMAGE" + exit 1 +fi + +# Check if service exists +kubectl get service web-svc -n dns-debug || { + echo "Service web-svc not found in namespace dns-debug" + exit 1 +} + +# Check service type +SERVICE_TYPE=$(kubectl get service web-svc -n dns-debug -o jsonpath='{.spec.type}') +if [[ "$SERVICE_TYPE" != "ClusterIP" ]]; then + echo "Incorrect service type. Expected ClusterIP, got $SERVICE_TYPE" + exit 1 +fi + +# Check service selector +SELECTOR=$(kubectl get service web-svc -n dns-debug -o jsonpath='{.spec.selector.app}') +if [[ "$SELECTOR" != "web-app" ]]; then + echo "Incorrect service selector. Expected app=web-app, got app=$SELECTOR" + exit 1 +fi + +# Check if pods are running +READY_PODS=$(kubectl get deployment web-app -n dns-debug -o jsonpath='{.status.readyReplicas}') +if [[ "$READY_PODS" != "3" ]]; then + echo "Not all pods are ready. Expected 3, got $READY_PODS" + exit 1 +fi + +echo "Deployment and service validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q9_s2_validate_dns_resolution.sh b/facilitator/assets/exams/cka/002/scripts/validation/q9_s2_validate_dns_resolution.sh new file mode 100644 index 0000000..2eaa873 --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q9_s2_validate_dns_resolution.sh @@ -0,0 +1,38 @@ +#!/bin/bash +set -e + +# Check if test pod exists +kubectl get pod dns-test -n dns-debug || { + echo "Pod dns-test not found in namespace dns-debug" + exit 1 +} + +# Check if pod is running +POD_STATUS=$(kubectl get pod dns-test -n dns-debug -o jsonpath='{.status.phase}') +if [[ "$POD_STATUS" != "Running" ]]; then + echo "Pod is not in Running state. Current state: $POD_STATUS" + exit 1 +fi + +# Test service DNS resolution +kubectl exec dns-test -n dns-debug -- wget -qO- http://web-svc || { + echo "Failed to resolve service DNS: web-svc.dns-debug.svc.cluster.local" + exit 1 +} + +# Test service DNS resolution +kubectl exec dns-test -n dns-debug -- wget -qO- http://web-svc.dns-debug.svc.cluster.local || { + echo "Failed to resolve service DNS: web-svc.dns-debug.svc.cluster.local" + exit 1 +} + +# Test pod DNS resolution (for first pod) +# POD_IP=$(kubectl get pod -l app=web-app -n dns-debug -o jsonpath='{.items[0].status.podIP}') +# POD_IP_REVERSED=$(echo $POD_IP | awk -F. '{print $4"."$3"."$2"."$1}') +# kubectl exec dns-test -n dns-debug -- nslookup $POD_IP_REVERSED.in-addr.arpa || { +# echo "Failed to resolve pod DNS" +# exit 1 +# } + +echo "DNS resolution validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/cka/002/scripts/validation/q9_s3_validate_dns_config.sh b/facilitator/assets/exams/cka/002/scripts/validation/q9_s3_validate_dns_config.sh new file mode 100644 index 0000000..f819b2f --- /dev/null +++ b/facilitator/assets/exams/cka/002/scripts/validation/q9_s3_validate_dns_config.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e + +# Check if ConfigMap exists +kubectl get configmap dns-config -n dns-debug || { + echo "ConfigMap dns-config not found in namespace dns-debug" + exit 1 +} + +# Check if test pod uses the ConfigMap +DNS_CONFIG=$(kubectl get pod dns-test -n dns-debug -o jsonpath='{.spec.dnsConfig}') +if [[ -z "$DNS_CONFIG" ]]; then + echo "Pod dns-test does not have custom DNS configuration" + exit 1 +fi + +# Check if pod has correct DNS policy +DNS_POLICY=$(kubectl get pod dns-test -n dns-debug -o jsonpath='{.spec.dnsPolicy}') +if [[ "$DNS_POLICY" != "None" ]] && [[ "$DNS_POLICY" != "ClusterFirst" ]]; then + echo "Incorrect DNS policy. Expected None or ClusterFirst, got $DNS_POLICY" + exit 1 +fi + +# Check if search domains are configured +SEARCH_DOMAINS=$(kubectl get pod dns-test -n dns-debug -o jsonpath='{.spec.dnsConfig.searches[*]}') +if [[ -z "$SEARCH_DOMAINS" ]]; then + echo "No search domains configured in pod's DNS config" + exit 1 +fi + +echo "DNS configuration validation successful" +exit 0 \ No newline at end of file diff --git a/facilitator/assets/exams/ckad/001/answers.md b/facilitator/assets/exams/ckad/001/answers.md index 179e87a..4add7da 100644 --- a/facilitator/assets/exams/ckad/001/answers.md +++ b/facilitator/assets/exams/ckad/001/answers.md @@ -115,13 +115,13 @@ spec: image: nginx volumeMounts: - name: log-volume - mountPath: /var/log + mountPath: /var/my-log - name: sidecar image: busybox - command: ["sh", "-c", "while true; do date >> /var/log/date.log; sleep 10; done"] + command: ["sh", "-c", "while true; do date >> /var/my-log/date.log; sleep 10; done"] volumeMounts: - name: log-volume - mountPath: /var/log + mountPath: /var/my-log volumes: - name: log-volume emptyDir: {} @@ -140,7 +140,7 @@ You can verify the pod is working correctly: kubectl get pod sidecar-pod -n troubleshooting # Verify the shared volume is mounted and the log file is being written -kubectl exec -it sidecar-pod -n troubleshooting -c nginx -- cat /var/log/date.log +kubectl exec -it sidecar-pod -n troubleshooting -c nginx -- cat /var/my-log/date.log # Check events related to the pod kubectl describe pod sidecar-pod -n troubleshooting diff --git a/facilitator/assets/exams/ckad/001/assessment.json b/facilitator/assets/exams/ckad/001/assessment.json index bfcd181..e4f75f4 100644 --- a/facilitator/assets/exams/ckad/001/assessment.json +++ b/facilitator/assets/exams/ckad/001/assessment.json @@ -168,7 +168,7 @@ "id": "6", "namespace": "troubleshooting", "machineHostname": "ckad9999", - "question": "The DevOps team needs a specialized pod to help with monitoring and logging. \n\nCreate a multi-container pod named `sidecar-pod` in the `troubleshooting` namespace with the following specifications: \n\n1. Main container: \n - Image: `nginx` \n - Purpose: Serve web content \n\n2. Sidecar container: \n - Image: `busybox` \n - Command: [`sh`, `-c`, `while true; do date >> /var/log/date.log; sleep 10; done`] \n\n3. Shared volume configuration: \n - Volume name: `log-volume` \n - Mount path: `/var/log` in both containers \n\nThis demonstrates the sidecar container pattern for extending application functionality.", + "question": "The DevOps team needs a specialized pod to help with monitoring and logging. \n\nCreate a multi-container pod named `sidecar-pod` in the `troubleshooting` namespace with the following specifications: \n\n1. Main container: \n - Image: `nginx`\n - Container name: `nginx` \n - Purpose: Serve web content \n\n2. Sidecar container: \n - Image: `busybox`\n - Container name: `sidecar` \n - Command: [`sh`, `-c`, `while true; do date >> /var/my-log/date.log; sleep 10; done`] \n\n3. Shared volume configuration: \n - Volume name: `log-volume` \n - Mount path: `/var/my-log` in both containers \n\nThis demonstrates the sidecar container pattern for extending application functionality.", "concepts": ["pods", "multi-container", "volumes", "sidecar-pattern"], "verification": [ { diff --git a/facilitator/assets/exams/labs.json b/facilitator/assets/exams/labs.json index 1c49b38..878b090 100644 --- a/facilitator/assets/exams/labs.json +++ b/facilitator/assets/exams/labs.json @@ -30,6 +30,16 @@ "difficulty": "Hard", "examDurationInMinutes": 120 }, + { + "id": "cka-002", + "assetPath": "assets/exams/cka/002", + "name": "CKA Practice Lab - Advanced Administration", + "category": "CKA", + "description": "Comprehensive lab covering advanced Kubernetes administration topics including storage, networking, security, and troubleshooting with focus on real-world scenarios, updated with the latest CKA exam curriculum changes.", + "warmUpTimeInSeconds": 360, + "difficulty": "Hard", + "examDurationInMinutes": 120 + }, { "id": "cka-001", "assetPath": "assets/exams/cka/001", diff --git a/facilitator/src/services/examService.js b/facilitator/src/services/examService.js index 4a405d6..1f10902 100644 --- a/facilitator/src/services/examService.js +++ b/facilitator/src/services/examService.js @@ -8,9 +8,7 @@ const path = require('path'); const { v4: uuidv4 } = require('uuid'); const logger = require('../utils/logger'); const redisClient = require('../utils/redisClient'); -const config = require('../config'); const jumphostService = require('./jumphostService'); -const sshService = require('./sshService'); const MetricService = require('./metricService'); /** @@ -55,7 +53,7 @@ async function createExam(examData) { logger.info(`Exam created successfully with ID: ${examId}`); // Determine number of nodes required for the exam (default to 1 if not specified) - const nodeCount = examData.nodeCount || 1; + const nodeCount = examData.config.workerNodes || 1; // Set up the exam environment asynchronously // This will happen in the background while the response is sent back to the client