mirror of
https://github.com/sailor-sh/CK-X.git
synced 2026-07-12 17:19:18 +00:00
ADD: CKA ADM Lab
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"lab": "cka-001",
|
||||
"workerNodes": 2,
|
||||
"workerNodes": 1,
|
||||
"answers": "assets/exams/cka/001/answers.md",
|
||||
"questions": "assessment.json",
|
||||
"totalMarks": 100,
|
||||
|
||||
1088
facilitator/assets/exams/cka/002/answers.md
Normal file
1088
facilitator/assets/exams/cka/002/answers.md
Normal file
File diff suppressed because it is too large
Load Diff
999
facilitator/assets/exams/cka/002/answers.sh
Normal file
999
facilitator/assets/exams/cka/002/answers.sh
Normal file
@@ -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 - <<EOF
|
||||
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
|
||||
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!"
|
||||
597
facilitator/assets/exams/cka/002/assessment.json
Normal file
597
facilitator/assets/exams/cka/002/assessment.json
Normal file
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
10
facilitator/assets/exams/cka/002/config.json
Normal file
10
facilitator/assets/exams/cka/002/config.json
Normal file
@@ -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
|
||||
}
|
||||
10
facilitator/assets/exams/cka/002/scripts/setup/q10_setup.sh
Normal file
10
facilitator/assets/exams/cka/002/scripts/setup/q10_setup.sh
Normal file
@@ -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"
|
||||
16
facilitator/assets/exams/cka/002/scripts/setup/q11_setup.sh
Normal file
16
facilitator/assets/exams/cka/002/scripts/setup/q11_setup.sh
Normal file
@@ -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"
|
||||
18
facilitator/assets/exams/cka/002/scripts/setup/q12_setup.sh
Normal file
18
facilitator/assets/exams/cka/002/scripts/setup/q12_setup.sh
Normal file
@@ -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 <<EOF
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- deployment.yaml
|
||||
EOF
|
||||
|
||||
echo "Setup completed for Question 12"
|
||||
14
facilitator/assets/exams/cka/002/scripts/setup/q13_setup.sh
Normal file
14
facilitator/assets/exams/cka/002/scripts/setup/q13_setup.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create namespace
|
||||
kubectl create namespace gateway --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Install Gateway API CRDs
|
||||
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v0.8.0/standard-install.yaml
|
||||
|
||||
# Wait for CRDs to be ready
|
||||
kubectl wait --for=condition=established --timeout=60s crd/gateways.gateway.networking.k8s.io
|
||||
kubectl wait --for=condition=established --timeout=60s crd/httproutes.gateway.networking.k8s.io
|
||||
|
||||
echo "Setup completed for Question 13"
|
||||
11
facilitator/assets/exams/cka/002/scripts/setup/q14_setup.sh
Normal file
11
facilitator/assets/exams/cka/002/scripts/setup/q14_setup.sh
Normal file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create namespace
|
||||
kubectl create namespace limits --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Remove any existing LimitRange or ResourceQuota
|
||||
kubectl delete limitrange --all -n limits 2>/dev/null || true
|
||||
kubectl delete resourcequota --all -n limits 2>/dev/null || true
|
||||
|
||||
echo "Setup completed for Question 14"
|
||||
16
facilitator/assets/exams/cka/002/scripts/setup/q15_setup.sh
Normal file
16
facilitator/assets/exams/cka/002/scripts/setup/q15_setup.sh
Normal file
@@ -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"
|
||||
@@ -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"
|
||||
16
facilitator/assets/exams/cka/002/scripts/setup/q17_setup.sh
Normal file
16
facilitator/assets/exams/cka/002/scripts/setup/q17_setup.sh
Normal file
@@ -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"
|
||||
13
facilitator/assets/exams/cka/002/scripts/setup/q18_setup.sh
Normal file
13
facilitator/assets/exams/cka/002/scripts/setup/q18_setup.sh
Normal file
@@ -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"
|
||||
29
facilitator/assets/exams/cka/002/scripts/setup/q19_setup.sh
Normal file
29
facilitator/assets/exams/cka/002/scripts/setup/q19_setup.sh
Normal file
@@ -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 - <<EOF
|
||||
# apiVersion: scheduling.k8s.io/v1
|
||||
# kind: PriorityClass
|
||||
# metadata:
|
||||
# name: high-priority
|
||||
# value: 1000
|
||||
# globalDefault: false
|
||||
# description: "High priority class for testing"
|
||||
# ---
|
||||
# apiVersion: scheduling.k8s.io/v1
|
||||
# kind: PriorityClass
|
||||
# metadata:
|
||||
# name: low-priority
|
||||
# value: 100
|
||||
# globalDefault: false
|
||||
# description: "Low priority class for testing"
|
||||
# EOF
|
||||
|
||||
echo "Setup completed for Question 19"
|
||||
17
facilitator/assets/exams/cka/002/scripts/setup/q1_setup.sh
Normal file
17
facilitator/assets/exams/cka/002/scripts/setup/q1_setup.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create namespace
|
||||
kubectl create namespace storage-task --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Ensure the standard storage class exists
|
||||
kubectl get storageclass standard || kubectl create -f - <<EOF
|
||||
apiVersion: storage.k8s.io/v1
|
||||
kind: StorageClass
|
||||
metadata:
|
||||
name: standard
|
||||
provisioner: rancher.io/local-path
|
||||
volumeBindingMode: WaitForFirstConsumer
|
||||
EOF
|
||||
|
||||
echo "Setup completed for Question 1"
|
||||
41
facilitator/assets/exams/cka/002/scripts/setup/q20_setup.sh
Normal file
41
facilitator/assets/exams/cka/002/scripts/setup/q20_setup.sh
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create namespace
|
||||
kubectl create namespace troubleshoot --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Create the failing deployment
|
||||
kubectl create -f - <<EOF
|
||||
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: 8080
|
||||
resources:
|
||||
limits:
|
||||
memory: "64Mi"
|
||||
cpu: "100m"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8080
|
||||
initialDelaySeconds: 3
|
||||
periodSeconds: 3
|
||||
EOF
|
||||
|
||||
echo "Setup completed for Question 20"
|
||||
18
facilitator/assets/exams/cka/002/scripts/setup/q2_setup.sh
Normal file
18
facilitator/assets/exams/cka/002/scripts/setup/q2_setup.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create namespace
|
||||
kubectl create namespace storage-class --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Create a dummy default storage class to test removal of default
|
||||
kubectl create -f - <<EOF
|
||||
apiVersion: storage.k8s.io/v1
|
||||
kind: StorageClass
|
||||
metadata:
|
||||
name: default-test
|
||||
annotations:
|
||||
storageclass.kubernetes.io/is-default-class: "true"
|
||||
provisioner: rancher.io/local-path
|
||||
EOF
|
||||
|
||||
echo "Setup completed for Question 2"
|
||||
13
facilitator/assets/exams/cka/002/scripts/setup/q3_setup.sh
Normal file
13
facilitator/assets/exams/cka/002/scripts/setup/q3_setup.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create namespace
|
||||
kubectl create namespace manual-storage --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Create the directory on the node (this assumes we have access to the node)
|
||||
mkdir -p /mnt/data
|
||||
|
||||
# Label the node for identification
|
||||
kubectl label node k3d-cluster-agent-0 kubernetes.io/hostname=k3d-cluster-agent-0 --overwrite
|
||||
|
||||
echo "Setup completed for Question 3"
|
||||
14
facilitator/assets/exams/cka/002/scripts/setup/q4_setup.sh
Normal file
14
facilitator/assets/exams/cka/002/scripts/setup/q4_setup.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create namespace
|
||||
kubectl create namespace scaling --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Enable metrics-server if not present
|
||||
kubectl get deployment metrics-server -n kube-system || {
|
||||
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
|
||||
# Wait for metrics-server to be ready
|
||||
kubectl wait --for=condition=available --timeout=180s deployment/metrics-server -n kube-system
|
||||
}
|
||||
|
||||
echo "Setup completed for Question 4"
|
||||
10
facilitator/assets/exams/cka/002/scripts/setup/q5_setup.sh
Normal file
10
facilitator/assets/exams/cka/002/scripts/setup/q5_setup.sh
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create namespace
|
||||
kubectl create namespace scheduling --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Ensure the target node exists and is labeled with hostname
|
||||
kubectl label node k3d-cluster-agent-1 kubernetes.io/hostname=k3d-cluster-agent-1 --overwrite
|
||||
|
||||
echo "Setup completed for Question 5"
|
||||
37
facilitator/assets/exams/cka/002/scripts/setup/q6_setup.sh
Normal file
37
facilitator/assets/exams/cka/002/scripts/setup/q6_setup.sh
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create namespace
|
||||
kubectl create namespace security --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Enable PodSecurity admission controller if not already enabled
|
||||
# Note: This might require cluster-level access and might not be possible in all environments
|
||||
|
||||
# Create the role and rolebinding for PSP
|
||||
kubectl create -f - <<EOF
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: psp-role
|
||||
namespace: security
|
||||
rules:
|
||||
- apiGroups: ['policy']
|
||||
resources: ['podsecuritypolicies']
|
||||
verbs: ['use']
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: psp-role-binding
|
||||
namespace: security
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: psp-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: default
|
||||
namespace: security
|
||||
EOF
|
||||
|
||||
echo "Setup completed for Question 6"
|
||||
13
facilitator/assets/exams/cka/002/scripts/setup/q7_setup.sh
Normal file
13
facilitator/assets/exams/cka/002/scripts/setup/q7_setup.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create namespace if not exists (reusing scheduling namespace from Q5)
|
||||
kubectl create namespace scheduling --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Ensure the node exists
|
||||
kubectl get node k3d-cluster-agent-0 || {
|
||||
echo "Required node k3d-cluster-agent-0 not found"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Setup completed for Question 7"
|
||||
17
facilitator/assets/exams/cka/002/scripts/setup/q8_setup.sh
Normal file
17
facilitator/assets/exams/cka/002/scripts/setup/q8_setup.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create namespace
|
||||
kubectl create namespace stateful --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Ensure storage class exists
|
||||
kubectl get storageclass cold || kubectl create -f - <<EOF
|
||||
apiVersion: storage.k8s.io/v1
|
||||
kind: StorageClass
|
||||
metadata:
|
||||
name: cold
|
||||
provisioner: rancher.io/local-path
|
||||
volumeBindingMode: WaitForFirstConsumer
|
||||
EOF
|
||||
|
||||
echo "Setup completed for Question 8"
|
||||
13
facilitator/assets/exams/cka/002/scripts/setup/q9_setup.sh
Normal file
13
facilitator/assets/exams/cka/002/scripts/setup/q9_setup.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Create namespace
|
||||
kubectl create namespace dns-debug --dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
# Ensure CoreDNS is running
|
||||
kubectl rollout status deployment/coredns -n kube-system --timeout=30s || {
|
||||
echo "CoreDNS is not running properly"
|
||||
exit 1
|
||||
}
|
||||
|
||||
echo "Setup completed for Question 9"
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Check if deployment exists and has correct replicas
|
||||
DEPLOY_STATUS=$(kubectl get deployment dns-app -n dns-config -o jsonpath='{.status.replicas},{.status.availableReplicas}' 2>/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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Create a test pod with the app-admin ServiceAccount
|
||||
cat <<EOF | kubectl apply -f -
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: rbac-test-pod
|
||||
namespace: cluster-admin
|
||||
spec:
|
||||
serviceAccountName: app-admin
|
||||
containers:
|
||||
- name: curl
|
||||
image: curlimages/curl
|
||||
command: ["sleep", "3600"]
|
||||
EOF
|
||||
|
||||
# Wait for pod to be running
|
||||
for i in {1..30}; do
|
||||
if kubectl get pod rbac-test-pod -n cluster-admin | grep -q Running; then
|
||||
break
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# Test pod operations (should succeed)
|
||||
LIST_PODS=$(kubectl auth can-i list pods --as=system:serviceaccount:cluster-admin:app-admin -n cluster-admin)
|
||||
GET_PODS=$(kubectl auth can-i get pods --as=system:serviceaccount:cluster-admin:app-admin -n cluster-admin)
|
||||
WATCH_PODS=$(kubectl auth can-i watch pods --as=system:serviceaccount:cluster-admin:app-admin -n cluster-admin)
|
||||
|
||||
# Test deployment operations (should succeed)
|
||||
LIST_DEPLOY=$(kubectl auth can-i list deployments --as=system:serviceaccount:cluster-admin:app-admin -n cluster-admin)
|
||||
GET_DEPLOY=$(kubectl auth can-i get deployments --as=system:serviceaccount:cluster-admin:app-admin -n cluster-admin)
|
||||
UPDATE_DEPLOY=$(kubectl auth can-i update deployments --as=system:serviceaccount:cluster-admin:app-admin -n cluster-admin)
|
||||
|
||||
# Test configmap operations (should succeed)
|
||||
CREATE_CM=$(kubectl auth can-i create configmaps --as=system:serviceaccount:cluster-admin:app-admin -n cluster-admin)
|
||||
DELETE_CM=$(kubectl auth can-i delete configmaps --as=system:serviceaccount:cluster-admin:app-admin -n cluster-admin)
|
||||
|
||||
# Clean up
|
||||
kubectl delete pod rbac-test-pod -n cluster-admin --force --grace-period=0 2>/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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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": [
|
||||
{
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user