kubestronaut-sim runs each exam task on a host of its own - a kind (kubeadm) cluster in rootless podman, or a QEMU/KVM Ubuntu VM for the CKS tasks that need their own kernel (Falco, AppArmor) - and puts a browser desktop in front of them, the way killer.sh and sailor.sh do. - ksim, sim/: the engine (sessions, drills, study mode, grading, coverage, audit), host and VM lifecycle, the desktop - images/: exam host images (common, CKS variant with its tools, VM guest) - cka/ (131 tasks), ckad/ (105), cks/ (110): task banks, curricula with study notes, reference solutions and scripted solutions Every domain of every track has at least 17 pool tasks. `./ksim audit` proves a task three ways: it seeds cleanly on its own host, scores nothing before anyone has worked on it, and reaches full marks when its scripted solution is run on the host as `candidate`. All 346 tasks pass (CKA audited 2026-10-13, CKAD and CKS 2026-10-12). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JZPd3GGWyxTgAkL6a4gAJz
1270 lines
50 KiB
Python
1270 lines
50 KiB
Python
"""Scripted solutions: what a candidate would type on the task's host, as a script.
|
|
|
|
`./ksim audit` runs a task's script as `candidate` in an interactive shell on its host (so the
|
|
host's aliases apply) after grading the task untouched, and grades it again: full marks prove the
|
|
task can be solved as its text describes, on a cluster of its own. %{param}, {WS} and {CP}.. are
|
|
substituted as in the task. A task without a script is only audited for a clean seed.
|
|
|
|
SOLVE: task id -> bash
|
|
"""
|
|
|
|
SOLVE = {
|
|
# ---- 1-37: the first banks (questions.py, bank_extra.py); 13 is further down
|
|
1: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: %{pod}, namespace: %{ns}}
|
|
spec:
|
|
volumes: [{name: spool, emptyDir: {}}]
|
|
containers:
|
|
- name: writer
|
|
image: busybox:1.36
|
|
command: [sh, -c, "while true; do echo 'metric 1' >> /spool/raw.txt; sleep 2; done"]
|
|
volumeMounts: [{name: spool, mountPath: /spool}]
|
|
- name: adapter
|
|
image: busybox:1.36
|
|
command: [sh, -c, "tail -f /spool/raw.txt"]
|
|
volumeMounts: [{name: spool, mountPath: /spool}]
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
sleep 6
|
|
""",
|
|
2: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: %{pod}, namespace: %{ns}}
|
|
spec:
|
|
initContainers:
|
|
- name: wait-for-backend
|
|
image: busybox:1.36
|
|
command: [sh, -c, "until wget -T 2 -q -O- http://backend:80 >/dev/null 2>&1; do echo waiting; sleep 2; done"]
|
|
containers: [{name: app, image: "nginx:1.27"}]
|
|
EOF
|
|
kubectl -n %{ns} create deploy backend --image=nginx:1.27
|
|
kubectl -n %{ns} rollout status deploy/backend --timeout=180s
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
""",
|
|
3: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: batch/v1
|
|
kind: CronJob
|
|
metadata: {name: %{cj}, namespace: %{ns}}
|
|
spec:
|
|
schedule: "*/%{m} * * * *"
|
|
startingDeadlineSeconds: 10
|
|
successfulJobsHistoryLimit: 2
|
|
failedJobsHistoryLimit: 2
|
|
jobTemplate:
|
|
spec:
|
|
activeDeadlineSeconds: 20
|
|
template:
|
|
spec:
|
|
restartPolicy: Never
|
|
containers:
|
|
- {name: digest, image: "busybox:1.36", command: [sh, -c, "echo digest"]}
|
|
EOF
|
|
""",
|
|
4: r"""
|
|
kubectl -n %{ns} set image deploy %{dep} nginx=nginx:1.27
|
|
kubectl -n %{ns} annotate deploy %{dep} kubernetes.io/change-cause='pick 1.27' --overwrite
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
kubectl -n %{ns} set image deploy %{dep} nginx=nginx:9.99-nope
|
|
sleep 10
|
|
kubectl -n %{ns} rollout undo deploy %{dep}
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
""",
|
|
5: r"""
|
|
kubectl -n %{ns} get deploy %{svc}-blue -o json | jq 'del(.status, .metadata.uid, .metadata.resourceVersion, .metadata.creationTimestamp, .metadata.generation, .metadata.annotations, .metadata.managedFields) | .metadata.name = "%{svc}-green" | .spec.replicas = 2 | .spec.selector.matchLabels.version = "green" | .spec.template.metadata.labels.version = "green" | .spec.template.spec.containers[0].image = "nginx:1.27"' | kubectl apply -f -
|
|
kubectl -n %{ns} rollout status deploy %{svc}-green --timeout=240s
|
|
kubectl -n %{ns} patch svc %{svc} -p '{"spec":{"selector":{"app":"%{svc}","version":"green"}}}'
|
|
sleep 5
|
|
""",
|
|
6: r"""
|
|
kubectl create ns %{ns} 2>/dev/null
|
|
helm -n %{ns} install %{rel} {WS}/q6/chart --set replicaCount=2 >/dev/null
|
|
helm -n %{ns} upgrade %{rel} {WS}/q6/chart --set replicaCount=4 >/dev/null
|
|
helm -n %{ns} rollback %{rel} 1 >/dev/null
|
|
helm -n %{ns} history %{rel} -o json | jq 'map(.revision) | max' > {WS}/q6/revision.txt
|
|
""",
|
|
7: r"""
|
|
kubectl -n %{ns} patch deploy %{dep} --type=json -p '[
|
|
{"op": "add", "path": "/spec/template/spec/containers/0/startupProbe", "value": {"httpGet": {"path": "/", "port": 80}, "periodSeconds": 5, "failureThreshold": 12}},
|
|
{"op": "add", "path": "/spec/template/spec/containers/0/readinessProbe", "value": {"httpGet": {"path": "/", "port": 80}, "periodSeconds": 5, "failureThreshold": 1}},
|
|
{"op": "add", "path": "/spec/template/spec/containers/0/livenessProbe", "value": {"tcpSocket": {"port": 80}, "periodSeconds": 10, "initialDelaySeconds": 15}}]'
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
""",
|
|
8: r"""
|
|
for i in $(seq 1 40); do R=$(kubectl -n %{ns} get pod %{pod} -o jsonpath='{.status.containerStatuses[0].state.waiting.reason}'); case "$R" in *ImagePull*) break;; esac; sleep 3; done
|
|
echo "$R" > {WS}/q8/reason.txt
|
|
kubectl -n %{ns} set image pod/%{pod} worker=busybox:1.36
|
|
for i in $(seq 1 60); do [ "$(kubectl -n %{ns} get pod %{pod} -o jsonpath='{.status.phase}')" = Running ] && [ "$(kubectl -n %{ns} get pod %{pod} -o jsonpath='{.status.containerStatuses[0].ready}')" = true ] && break; sleep 3; done
|
|
""",
|
|
9: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: %{pod}, namespace: %{ns}}
|
|
spec:
|
|
securityContext: {runAsUser: %{uid}, runAsGroup: %{uid}, runAsNonRoot: true}
|
|
containers:
|
|
- name: c
|
|
image: busybox:1.36
|
|
command: [sleep, "86400"]
|
|
securityContext:
|
|
readOnlyRootFilesystem: true
|
|
allowPrivilegeEscalation: false
|
|
capabilities: {drop: [ALL], add: [NET_BIND_SERVICE]}
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
""",
|
|
10: r"""
|
|
kubectl -n %{ns} create cm app-cfg --from-literal=mode=live
|
|
kubectl -n %{ns} create secret generic app-sec --from-literal=apikey=xyz789
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: %{pod}, namespace: %{ns}}
|
|
spec:
|
|
volumes:
|
|
- name: bundle
|
|
projected:
|
|
sources:
|
|
- configMap: {name: app-cfg, items: [{key: mode, path: mode}]}
|
|
- secret: {name: app-sec, items: [{key: apikey, path: apikey}]}
|
|
- serviceAccountToken: {audience: vault, expirationSeconds: 3600, path: token}
|
|
containers:
|
|
- name: c
|
|
image: busybox:1.36
|
|
command: [sleep, "86400"]
|
|
volumeMounts: [{name: bundle, mountPath: /etc/bundle}]
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
""",
|
|
11: r"""
|
|
kubectl -n %{ns} expose deploy %{dep} --port=8080 --target-port=80
|
|
kubectl -n %{ns} expose deploy %{dep} --name=%{dep}-ext --type=NodePort --port=80 --target-port=80 --dry-run=client -o json | jq '.spec.ports[0].nodePort = %{np}' | kubectl apply -f -
|
|
kubectl -n %{ns} create ingress %{dep} --class=nginx --rule="%{dep}.example.com/*=%{dep}:8080"
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
""",
|
|
12: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata: {name: db-allow, namespace: %{ns}}
|
|
spec:
|
|
podSelector: {matchLabels: {app: db}}
|
|
policyTypes: [Ingress]
|
|
ingress:
|
|
- from: [{podSelector: {matchLabels: {app: %{client}}}}]
|
|
ports: [{protocol: TCP, port: 5432}]
|
|
EOF
|
|
""",
|
|
14: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata: {name: %{job}, namespace: %{ns}}
|
|
spec:
|
|
completions: %{n}
|
|
parallelism: %{p}
|
|
backoffLimit: 2
|
|
activeDeadlineSeconds: 300
|
|
template:
|
|
metadata: {labels: {batch: %{job}}}
|
|
spec:
|
|
restartPolicy: Never
|
|
containers:
|
|
- {name: render, image: "busybox:1.36", command: [sh, -c, "echo frame done; sleep 3"]}
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Complete job/%{job} --timeout=240s
|
|
""",
|
|
15: r"""
|
|
cat <<'EOF' | kubectl apply -f -
|
|
apiVersion: apps/v1
|
|
kind: DaemonSet
|
|
metadata: {name: %{ds}, namespace: %{ns}}
|
|
spec:
|
|
selector: {matchLabels: {app: %{ds}}}
|
|
template:
|
|
metadata: {labels: {app: %{ds}}}
|
|
spec:
|
|
tolerations: [{key: node-role.kubernetes.io/control-plane, operator: Exists, effect: NoSchedule}]
|
|
containers:
|
|
- name: agent
|
|
image: busybox:1.36
|
|
command: ["sh", "-c", "while true; do echo \"$(date) $NODE_NAME ok\"; sleep 30; done"]
|
|
env: [{name: NODE_NAME, valueFrom: {fieldRef: {fieldPath: spec.nodeName}}}]
|
|
resources: {requests: {cpu: 10m, memory: 16Mi}}
|
|
EOF
|
|
kubectl -n %{ns} rollout status ds %{ds} --timeout=240s
|
|
sleep 3
|
|
""",
|
|
16: r"""
|
|
cd {WS}/q16
|
|
yq -i '.spec.template.spec.initContainers = [(.spec.template.spec.containers[] | select(.name == "shipper")) * {"restartPolicy": "Always"}] | .spec.template.spec.containers |= map(select(.name == "worker"))' job.yaml
|
|
kubectl -n %{ns} delete job %{job}
|
|
kubectl apply -f job.yaml
|
|
kubectl -n %{ns} wait --for=condition=Complete job/%{job} --timeout=240s
|
|
""",
|
|
17: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata: {name: %{pvc}, namespace: %{ns}}
|
|
spec: {accessModes: [ReadWriteOnce], storageClassName: standard, resources: {requests: {storage: 100Mi}}}
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata: {name: %{dep}, namespace: %{ns}, labels: {app: %{dep}}}
|
|
spec:
|
|
replicas: 1
|
|
selector: {matchLabels: {app: %{dep}}}
|
|
template:
|
|
metadata: {labels: {app: %{dep}}}
|
|
spec:
|
|
terminationGracePeriodSeconds: 2
|
|
volumes:
|
|
- {name: data, persistentVolumeClaim: {claimName: %{pvc}}}
|
|
- {name: scratch, emptyDir: {medium: Memory, sizeLimit: 32Mi}}
|
|
containers:
|
|
- name: busybox
|
|
image: busybox:1.36
|
|
command: ["sh", "-c", "date >> /data/boot.log; sleep 86400"]
|
|
volumeMounts: [{name: data, mountPath: /data}, {name: scratch, mountPath: /scratch}]
|
|
EOF
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
kubectl -n %{ns} delete pod -l app=%{dep}
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
sleep 5
|
|
""",
|
|
18: r"""
|
|
kubectl -n %{ns} get deploy %{app}-stable -o json | jq 'del(.status, .metadata.uid, .metadata.resourceVersion, .metadata.creationTimestamp, .metadata.generation, .metadata.annotations, .metadata.managedFields) | .metadata.name = "%{app}-canary" | .spec.replicas = 1 | .spec.selector.matchLabels.track = "canary" | .spec.template.metadata.labels.track = "canary" | .spec.template.spec.containers[0].image = "nginx:1.27"' | kubectl apply -f -
|
|
kubectl -n %{ns} scale deploy %{app}-stable --replicas=4
|
|
kubectl -n %{ns} rollout status deploy %{app}-canary --timeout=240s
|
|
kubectl -n %{ns} rollout status deploy %{app}-stable --timeout=240s
|
|
sleep 5
|
|
""",
|
|
19: r"""
|
|
kubectl -n %{ns} patch deploy %{dep} -p '{"spec":{"minReadySeconds":10,"revisionHistoryLimit":3,"strategy":{"type":"RollingUpdate","rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'
|
|
kubectl -n %{ns} set image deploy %{dep} nginx=nginx:1.27
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=300s
|
|
""",
|
|
20: r"""
|
|
mkdir -p {WS}/q20/overlays/prod
|
|
cat > {WS}/q20/overlays/prod/kustomization.yaml <<EOF
|
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
kind: Kustomization
|
|
resources: [../../base]
|
|
namespace: %{ns}
|
|
namePrefix: prod-
|
|
labels:
|
|
- pairs: {env: prod}
|
|
includeSelectors: false
|
|
replicas:
|
|
- {name: web, count: %{n}}
|
|
images:
|
|
- {name: nginx, newTag: "1.27"}
|
|
EOF
|
|
kubectl apply -k {WS}/q20/overlays/prod
|
|
kubectl -n %{ns} rollout status deploy prod-web --timeout=240s
|
|
""",
|
|
21: r"""
|
|
helm show values {WS}/q21/charts/webapp-1.3.0.tgz > {WS}/q21/defaults-1.3.0.yaml
|
|
printf 'replicaCount: 2\nservice:\n type: NodePort\n' > {WS}/q21/%{rel}-values.yaml
|
|
helm -n %{ns} install %{rel} {WS}/q21/charts/webapp-1.3.0.tgz -f {WS}/q21/%{rel}-values.yaml >/dev/null
|
|
helm -n %{ns} upgrade %{old} {WS}/q21/charts/webapp-1.3.0.tgz --reset-then-reuse-values >/dev/null
|
|
kubectl -n %{ns} rollout status deploy %{old}-webapp --timeout=240s
|
|
kubectl -n %{ns} rollout status deploy %{rel}-webapp --timeout=240s
|
|
""",
|
|
22: r"""
|
|
cd {WS}/q22
|
|
printf '%s\n' extensions/v1beta1 batch/v1beta1 policy/v1beta1 autoscaling/v2beta2 > removed.txt
|
|
cat > fixed.yaml <<EOF
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: Ingress
|
|
metadata: {name: %{app}, namespace: %{ns}}
|
|
spec:
|
|
rules:
|
|
- host: %{app}.example.com
|
|
http:
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
backend: {service: {name: %{app}, port: {number: 80}}}
|
|
---
|
|
apiVersion: batch/v1
|
|
kind: CronJob
|
|
metadata: {name: %{app}-report, namespace: %{ns}}
|
|
spec:
|
|
schedule: "15 2 * * *"
|
|
jobTemplate:
|
|
spec:
|
|
template:
|
|
spec:
|
|
restartPolicy: OnFailure
|
|
containers: [{name: report, image: "busybox:1.36", command: [sh, -c, "echo report"]}]
|
|
---
|
|
apiVersion: policy/v1
|
|
kind: PodDisruptionBudget
|
|
metadata: {name: %{app}, namespace: %{ns}}
|
|
spec:
|
|
minAvailable: 1
|
|
selector: {matchLabels: {app: %{app}}}
|
|
---
|
|
apiVersion: autoscaling/v2
|
|
kind: HorizontalPodAutoscaler
|
|
metadata: {name: %{app}, namespace: %{ns}}
|
|
spec:
|
|
scaleTargetRef: {apiVersion: apps/v1, kind: Deployment, name: %{app}}
|
|
minReplicas: 2
|
|
maxReplicas: 5
|
|
metrics:
|
|
- type: Resource
|
|
resource: {name: cpu, target: {type: Utilization, averageUtilization: 70}}
|
|
EOF
|
|
kubectl apply -f fixed.yaml
|
|
""",
|
|
23: r"""
|
|
for i in $(seq 1 60); do [ "$(kubectl top pod -n %{ns} --no-headers 2>/dev/null | wc -l)" -ge 3 ] && break; sleep 5; done
|
|
sleep 20
|
|
kubectl top pod -n %{ns} --sort-by=cpu --no-headers | head -1 | awk '{print $1}' > {WS}/q23/cpu.txt
|
|
kubectl top pod -n %{ns} --sort-by=memory --no-headers | head -1 | awk '{print $1}' > {WS}/q23/memory.txt
|
|
echo 'kubectl top node' > {WS}/q23/nodes.sh
|
|
echo 'kubectl top pod -A --sort-by=memory' > {WS}/q23/pods.sh
|
|
""",
|
|
24: r"""
|
|
for i in $(seq 1 40); do kubectl -n %{ns} logs %{pod} -c ingest --previous 2>/dev/null | grep -q FATAL && break; sleep 3; done
|
|
kubectl -n %{ns} logs %{pod} -c ingest --previous > {WS}/q24/crash.log
|
|
grep -o '/etc/ingest/[^ ]*\.yaml' {WS}/q24/crash.log | head -1 > {WS}/q24/missing.txt
|
|
kubectl -n %{ns} logs %{pod} -c helper --tail=3 --timestamps > {WS}/q24/helper.log
|
|
""",
|
|
25: r"""
|
|
kubectl -n %{ns} get deploy %{dep} -o json | jq '(.spec.template.spec.containers[0].env[] | select(.name == "LOG_LEVEL") | .valueFrom.configMapKeyRef.key) = "log_level" | del(.spec.template.spec.containers[0].command)' | kubectl apply -f -
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
kubectl -n %{ns} exec deploy/%{dep} -- printenv LOG_LEVEL > {WS}/q25/log-level.txt
|
|
""",
|
|
26: r"""
|
|
for i in $(seq 1 40); do R=$(kubectl -n %{ns} get pod -l app=%{dep} -o jsonpath='{.items[0].status.containerStatuses[0].lastState.terminated.reason}' 2>/dev/null); [ -n "$R" ] && break; sleep 3; done
|
|
echo $R > {WS}/q26/reason.txt
|
|
kubectl -n %{ns} set resources deploy %{dep} -c resize --limits=memory=256Mi
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
sleep 35
|
|
""",
|
|
27: r"""
|
|
kubectl get crd -o name | grep -i backup | cut -d/ -f2 > {WS}/q27/crd.txt
|
|
kubectl get backups.data.example.com -A --no-headers 2>/dev/null | wc -l > {WS}/q27/count.txt
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: data.example.com/v1
|
|
kind: Backup
|
|
metadata: {name: %{name}, namespace: %{ns}}
|
|
spec: {target: %{target}, schedule: "30 3 * * *", retentionDays: 14}
|
|
EOF
|
|
""",
|
|
28: r"""
|
|
kubectl -n %{ns} create sa %{sa}
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: Role
|
|
metadata: {name: %{sa}-role, namespace: %{ns}}
|
|
rules:
|
|
- {apiGroups: [""], resources: [pods, pods/log], verbs: [get, list, watch]}
|
|
- {apiGroups: [apps], resources: [deployments, deployments/scale], verbs: [get, list, update, patch]}
|
|
EOF
|
|
kubectl -n %{ns} create rolebinding %{sa}-binding --role=%{sa}-role --serviceaccount=%{ns}:%{sa}
|
|
""",
|
|
29: r"""
|
|
kubectl get ns %{ns} -o jsonpath='{.metadata.labels.pod-security\.kubernetes\.io/enforce}' > {WS}/q29/level.txt
|
|
kubectl -n %{ns} patch deploy %{dep} --type=json -p '[
|
|
{"op": "add", "path": "/spec/template/spec/securityContext", "value": {"runAsNonRoot": true, "runAsUser": %{uid}, "seccompProfile": {"type": "RuntimeDefault"}}},
|
|
{"op": "add", "path": "/spec/template/spec/containers/0/securityContext", "value": {"allowPrivilegeEscalation": false, "capabilities": {"drop": ["ALL"]}}}]'
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
""",
|
|
30: r"""
|
|
kubectl -n %{ns} create quota compute-quota --hard=requests.cpu=1,requests.memory=1Gi,limits.cpu=2,limits.memory=2Gi,pods=6
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: LimitRange
|
|
metadata: {name: container-defaults, namespace: %{ns}}
|
|
spec:
|
|
limits:
|
|
- type: Container
|
|
defaultRequest: {cpu: 100m, memory: 128Mi}
|
|
default: {cpu: 250m, memory: 256Mi}
|
|
max: {cpu: 500m, memory: 512Mi}
|
|
EOF
|
|
kubectl -n %{ns} create deploy %{dep} --image=nginx:1.27 --replicas=3
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
sleep 5
|
|
""",
|
|
31: r"""
|
|
kubectl -n %{ns} set resources deploy %{dep} -c app --requests=cpu=250m,memory=128Mi --limits=cpu=250m,memory=128Mi
|
|
kubectl -n %{ns} set resources deploy %{dep} -c metrics --requests=cpu=50m,memory=32Mi --limits=cpu=50m,memory=32Mi
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
sleep 40
|
|
kubectl -n %{ns} get pod -l app=%{dep} --field-selector status.phase=Running --sort-by=.metadata.creationTimestamp -o jsonpath='{.items[-1:].status.qosClass}' > {WS}/q31/qos.txt
|
|
""",
|
|
32: r"""
|
|
kubectl -n %{ns} create cm %{cm} --from-file=app.properties={WS}/q32/app.properties --from-literal=TIER=backend
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: %{pod}, namespace: %{ns}}
|
|
spec:
|
|
volumes:
|
|
- name: cfg
|
|
configMap: {name: %{cm}, items: [{key: app.properties, path: app.properties}]}
|
|
containers:
|
|
- name: reader
|
|
image: busybox:1.36
|
|
command: [sleep, "86400"]
|
|
env: [{name: APP_TIER, valueFrom: {configMapKeyRef: {name: %{cm}, key: TIER}}}]
|
|
volumeMounts: [{name: cfg, mountPath: /etc/app}]
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
""",
|
|
33: r"""
|
|
kubectl -n %{ns} get secret %{old} -o jsonpath='{.data.password}' | base64 -d > {WS}/q33/old-password.txt
|
|
kubectl -n %{ns} create secret generic %{sec} --from-literal=username=%{user} --from-literal=password=%{pw}
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: %{pod}, namespace: %{ns}}
|
|
spec:
|
|
volumes:
|
|
- name: creds
|
|
secret: {secretName: %{sec}, defaultMode: 0400}
|
|
containers:
|
|
- name: client
|
|
image: busybox:1.36
|
|
command: [sleep, "86400"]
|
|
env: [{name: API_USER, valueFrom: {secretKeyRef: {name: %{sec}, key: username}}}]
|
|
volumeMounts: [{name: creds, mountPath: /etc/api-creds, readOnly: true}]
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
""",
|
|
34: r"""
|
|
kubectl -n %{ns} create sa %{sa}
|
|
kubectl -n %{ns} patch sa %{sa} -p '{"automountServiceAccountToken": false}'
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: %{sa}-token
|
|
namespace: %{ns}
|
|
annotations: {kubernetes.io/service-account.name: %{sa}}
|
|
type: kubernetes.io/service-account-token
|
|
EOF
|
|
sleep 4
|
|
kubectl -n %{ns} get secret %{sa}-token -o jsonpath='{.data.token}' | base64 -d > {WS}/q34/token.txt
|
|
kubectl -n %{ns} create deploy %{dep} --image=nginx:1.27 --dry-run=client -o json | jq '.spec.template.spec.serviceAccountName = "%{sa}"' | kubectl apply -f -
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
""",
|
|
35: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata: {name: api-egress, namespace: %{ns}}
|
|
spec:
|
|
podSelector: {matchLabels: {app: api}}
|
|
policyTypes: [Egress]
|
|
egress:
|
|
- to: [{podSelector: {matchLabels: {app: db}}}]
|
|
ports: [{protocol: TCP, port: 80}]
|
|
- ports: [{protocol: UDP, port: 53}, {protocol: TCP, port: 53}]
|
|
EOF
|
|
sleep 6
|
|
""",
|
|
36: r"""
|
|
kubectl -n %{ns} patch svc %{app} --type=json -p '[{"op": "replace", "path": "/spec/selector/tier", "value": "web"}, {"op": "replace", "path": "/spec/ports/0/targetPort", "value": 80}]'
|
|
sleep 5
|
|
kubectl -n %{ns} get endpointslices -l kubernetes.io/service-name=%{app} -o jsonpath='{range .items[*].endpoints[*]}{.addresses[0]}{"\n"}{end}' > {WS}/q36/endpoints.txt
|
|
""",
|
|
37: r"""
|
|
kubectl -n %{ns} create secret tls %{dom}-tls --cert={WS}/q37/tls.crt --key={WS}/q37/tls.key
|
|
kubectl -n %{ns} create ingress %{dom} --class=nginx --rule="%{dom}.example.com/api*=api:8080,tls=%{dom}-tls" --rule="%{dom}.example.com/*=web:80,tls=%{dom}-tls"
|
|
""",
|
|
# ---- 38-57: the fixed set A (bank_sim_a.py); 41 is further down
|
|
38: r"""
|
|
kubectl -n danube run ferry --image=httpd:2-alpine --labels=tier=edge --dry-run=client -o json | jq '.spec.containers[0].name = "ferry-web"' | kubectl apply -f -
|
|
kubectl -n danube wait --for=condition=Ready pod/ferry --timeout=180s
|
|
mkdir -p {WS}/q38
|
|
echo "kubectl -n danube get pod ferry -o jsonpath='{.status.phase}'" > {WS}/q38/ferry-status.sh
|
|
""",
|
|
39: r"""
|
|
kubectl -n rhine create cronjob tide-report --image=busybox:1.36 --schedule='*/30 * * * *' --dry-run=client -o json -- sh -c 'echo "tide report $(date +%H:%M)"' | jq '.spec.suspend = true' | kubectl apply -f -
|
|
kubectl -n rhine create job tide-report-manual --from=cronjob/tide-report
|
|
kubectl -n rhine wait --for=condition=complete job/tide-report-manual --timeout=180s
|
|
mkdir -p {WS}/q39
|
|
kubectl -n rhine logs job/tide-report-manual > {WS}/q39/manual-run.log
|
|
""",
|
|
40: r"""
|
|
cat <<'EOF' | kubectl apply -f -
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata: {name: harbor-web, namespace: volga, labels: {app: harbor-web}}
|
|
spec:
|
|
replicas: 2
|
|
selector: {matchLabels: {app: harbor-web}}
|
|
template:
|
|
metadata: {labels: {app: harbor-web}}
|
|
spec:
|
|
volumes: [{name: html, emptyDir: {}}]
|
|
initContainers:
|
|
- name: page-gen
|
|
image: busybox:1.36
|
|
env: [{name: POD_NAME, valueFrom: {fieldRef: {fieldPath: metadata.name}}}]
|
|
command: [sh, -c, 'echo "harbor online: $POD_NAME" > /work/index.html']
|
|
volumeMounts: [{name: html, mountPath: /work}]
|
|
containers:
|
|
- name: nginx
|
|
image: nginx:1.27
|
|
volumeMounts: [{name: html, mountPath: /usr/share/nginx/html}]
|
|
EOF
|
|
kubectl -n volga expose deploy harbor-web --port=80
|
|
kubectl -n volga rollout status deploy harbor-web --timeout=240s
|
|
sleep 5
|
|
""",
|
|
42: r"""
|
|
kubectl -n yukon get deploy dispatch-api -o jsonpath='{.spec.template.spec.containers[0].image}' > {WS}/q42/broken-image.txt
|
|
kubectl -n yukon rollout undo deploy dispatch-api
|
|
kubectl -n yukon rollout status deploy dispatch-api --timeout=240s
|
|
kubectl -n yukon get deploy dispatch-api -o jsonpath='{.metadata.annotations.deployment\.kubernetes\.io/revision}' > {WS}/q42/revision.txt
|
|
""",
|
|
43: r"""
|
|
helm -n mekong list --failed -q > {WS}/q43/failed.txt
|
|
helm -n mekong uninstall buoy-north >/dev/null
|
|
helm -n mekong upgrade buoy-west {WS}/q43/charts/buoy-0.2.0.tgz --reuse-values --set replicaCount=3 >/dev/null
|
|
helm -n mekong rollback buoy-east 1 >/dev/null
|
|
kubectl -n mekong rollout status deploy buoy-west --timeout=240s
|
|
kubectl -n mekong rollout status deploy buoy-east --timeout=240s
|
|
""",
|
|
44: r"""
|
|
C=$(kubectl create --dry-run=client -f {WS}/q44/gauge-pod.yaml -o json | jq -c '.spec.containers[0] | .securityContext = {allowPrivilegeEscalation: false, privileged: false}')
|
|
kubectl -n ganges create deploy gauge-sensor --image=busybox:1.36 --replicas=3 --dry-run=client -o json | jq --argjson c "$C" '.spec.template.spec.containers = [$c]' > {WS}/q44/gauge-deployment.yaml
|
|
kubectl apply -f {WS}/q44/gauge-deployment.yaml
|
|
kubectl -n ganges delete pod gauge-sensor --grace-period=1
|
|
kubectl -n ganges rollout status deploy gauge-sensor --timeout=240s
|
|
""",
|
|
45: r"""
|
|
cd {WS}/q45/overlays/staging
|
|
cat > kustomization.yaml <<EOF
|
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
kind: Kustomization
|
|
resources: [../../base]
|
|
namespace: orinoco
|
|
configMapGenerator:
|
|
- name: river-api-settings
|
|
envs: [settings.env]
|
|
patches:
|
|
- path: patch.yaml
|
|
replicas:
|
|
- {name: river-api, count: 2}
|
|
EOF
|
|
cat > patch.yaml <<EOF
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata: {name: river-api}
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: river-api
|
|
envFrom: [{configMapRef: {name: river-api-settings}}]
|
|
resources: {limits: {memory: 128Mi}}
|
|
EOF
|
|
kubectl apply -k .
|
|
kubectl -n orinoco rollout status deploy river-api --timeout=240s
|
|
""",
|
|
46: r"""
|
|
kubectl -n tagus patch deploy tide-api --type=json -p '[
|
|
{"op": "add", "path": "/spec/template/spec/containers/0/readinessProbe", "value": {"exec": {"command": ["test", "-f", "/tmp/warm"]}, "periodSeconds": 5}},
|
|
{"op": "add", "path": "/spec/template/spec/containers/0/livenessProbe", "value": {"httpGet": {"path": "/healthz", "port": 8080}, "initialDelaySeconds": 30, "periodSeconds": 10, "failureThreshold": 3}}]'
|
|
kubectl -n tagus rollout status deploy tide-api --timeout=300s
|
|
""",
|
|
47: r"""
|
|
kubectl -n elbe debug vault-shim --image=busybox:1.36 --target=main -c dbg -- sh -c "tr '\0' '\n' < /proc/1/environ | grep SHIM_TOKEN; sleep 3600"
|
|
for i in $(seq 1 40); do T=$(kubectl -n elbe logs vault-shim -c dbg 2>/dev/null | sed -n 's/^SHIM_TOKEN=//p'); [ -n "$T" ] && break; sleep 3; done
|
|
echo "$T" > {WS}/q47/token.txt
|
|
""",
|
|
48: r"""
|
|
for i in $(seq 1 60); do
|
|
best=$(for n in seine amur; do kubectl -n $n get pod -o json | jq -r --arg n $n '.items[] | "\(.status.containerStatuses[0].restartCount // 0) \($n)/\(.metadata.name)"'; done | sort -rn | head -1)
|
|
[ "${best%% *}" -ge 2 ] 2>/dev/null && kubectl -n seine get events --field-selector type=Warning --no-headers 2>/dev/null | grep -q . && break
|
|
sleep 3
|
|
done
|
|
P=${best#* }
|
|
echo "$P" > {WS}/q48/most-restarts.txt
|
|
kubectl -n ${P%%/*} get pod ${P#*/} -o jsonpath='{.status.containerStatuses[0].lastState.terminated.exitCode}' > {WS}/q48/exit-code.txt
|
|
echo 'kubectl -n seine get events --field-selector type=Warning --sort-by=.lastTimestamp' > {WS}/q48/warnings.sh
|
|
""",
|
|
49: r"""
|
|
kubectl -n nile create role configmap-reader --verb=get,list,watch --resource=configmaps
|
|
kubectl -n nile create rolebinding ledger-sync-configmaps --role=configmap-reader --serviceaccount=nile:ledger-sync
|
|
for i in $(seq 1 40); do kubectl -n nile logs deploy/ledger-sync --tail=1 2>/dev/null | grep -q 'can list configmaps' && break; sleep 3; done
|
|
kubectl -n nile logs deploy/ledger-sync --tail=1 > {WS}/q49/log.txt
|
|
""",
|
|
50: r"""
|
|
kubectl -n indus create secret generic gatekeeper-tls-settings --from-file=tls-settings.conf={WS}/q50/tls-settings.conf --dry-run=client -o json | jq '.immutable = true' | kubectl apply -f -
|
|
kubectl -n indus patch deploy gatekeeper --type=json -p '[
|
|
{"op": "add", "path": "/spec/template/spec/volumes", "value": [{"name": "tls", "secret": {"secretName": "gatekeeper-tls-settings"}}]},
|
|
{"op": "add", "path": "/spec/template/spec/containers/0/volumeMounts", "value": [{"name": "tls", "mountPath": "/etc/nginx/conf.d/tls-settings.conf", "subPath": "tls-settings.conf"}]}]'
|
|
kubectl -n indus rollout status deploy gatekeeper --timeout=240s
|
|
""",
|
|
51: r"""
|
|
cat > {WS}/q51/default.conf <<'EOF'
|
|
server {
|
|
listen 80;
|
|
location /status { return 200 "harbor v2 ok"; }
|
|
location / { return 200 "harbor v1"; }
|
|
}
|
|
EOF
|
|
kubectl -n niger create cm harbor-nginx --from-file=default.conf={WS}/q51/default.conf --dry-run=client -o yaml | kubectl apply -f -
|
|
kubectl -n niger rollout restart deploy harbor-proxy
|
|
kubectl -n niger rollout status deploy harbor-proxy --timeout=240s
|
|
for i in 1 2 3 4 5; do
|
|
kubectl -n niger run tmp --rm -i --restart=Never --image=nginx:1.27 -- curl -s http://harbor-proxy.niger.svc.cluster.local/status > {WS}/q51/status.txt </dev/null 2>/dev/null
|
|
grep -q 'harbor v2 ok' {WS}/q51/status.txt && break; kubectl -n niger delete pod tmp --ignore-not-found >/dev/null 2>&1; sleep 3
|
|
done
|
|
""",
|
|
52: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: ledger-pair, namespace: murray}
|
|
spec:
|
|
securityContext: {runAsNonRoot: true, fsGroup: 3000}
|
|
volumes: [{name: data, emptyDir: {}}]
|
|
containers:
|
|
- name: writer-a
|
|
image: busybox:1.36
|
|
securityContext: {runAsUser: 1000}
|
|
command: [sh, -c, "echo a > /data/a.txt; sleep 86400"]
|
|
volumeMounts: [{name: data, mountPath: /data}]
|
|
- name: writer-b
|
|
image: busybox:1.36
|
|
securityContext: {runAsUser: 2000}
|
|
command: [sh, -c, "echo b > /data/b.txt; sleep 86400"]
|
|
volumeMounts: [{name: data, mountPath: /data}]
|
|
EOF
|
|
kubectl -n murray wait --for=condition=Ready pod/ledger-pair --timeout=180s
|
|
sleep 3
|
|
""",
|
|
53: r"""
|
|
echo LimitRange/douro-limits > {WS}/q53/rejected-by.txt
|
|
kubectl -n douro set resources deploy batch-runner -c runner --limits=memory=256Mi --requests=memory=128Mi
|
|
kubectl -n douro rollout status deploy batch-runner --timeout=240s
|
|
""",
|
|
54: r"""
|
|
kubectl -n oder expose pod buoy-api --name=buoy-svc --port=8080 --target-port=80
|
|
sleep 3
|
|
for i in 1 2 3 4 5; do
|
|
kubectl -n oder run tmp --rm -i --restart=Never --image=nginx:1.27 -- curl -s http://buoy-svc:8080 > {WS}/q54/response.html </dev/null 2>/dev/null
|
|
grep -qi 'Welcome to nginx' {WS}/q54/response.html && break; kubectl -n oder delete pod tmp --ignore-not-found >/dev/null 2>&1; sleep 3
|
|
done
|
|
kubectl -n oder delete pod tmp --ignore-not-found >/dev/null 2>&1
|
|
kubectl -n oder logs buoy-api > {WS}/q54/buoy.log
|
|
""",
|
|
55: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata: {name: vault-ingress, namespace: zambezi}
|
|
spec:
|
|
podSelector: {matchLabels: {app: vault}}
|
|
policyTypes: [Ingress]
|
|
ingress:
|
|
- from:
|
|
- podSelector: {matchLabels: {app: teller}}
|
|
- namespaceSelector: {matchLabels: {purpose: monitoring}}
|
|
ports: [{protocol: TCP, port: 80}]
|
|
EOF
|
|
sleep 6
|
|
""",
|
|
56: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata: {name: lighthouse, namespace: ebro}
|
|
spec: {clusterIP: None, selector: {app: lighthouse}, ports: [{port: 80}]}
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: StatefulSet
|
|
metadata: {name: lighthouse, namespace: ebro}
|
|
spec:
|
|
serviceName: lighthouse
|
|
replicas: 3
|
|
selector: {matchLabels: {app: lighthouse}}
|
|
template:
|
|
metadata: {labels: {app: lighthouse}}
|
|
spec: {containers: [{name: nginx, image: "nginx:1.27"}]}
|
|
EOF
|
|
kubectl -n ebro rollout status sts lighthouse --timeout=300s
|
|
F=lighthouse-1.lighthouse.ebro.svc.cluster.local
|
|
echo $F > {WS}/q56/fqdn.txt
|
|
for i in 1 2 3 4 5; do
|
|
IP=$(kubectl -n ebro run tmp --rm -i --restart=Never --image=busybox:1.36 -- nslookup $F </dev/null 2>/dev/null | awk '/^Name:/ {n=1} n && /^Address/ {print $NF; exit}')
|
|
[ -n "$IP" ] && break; kubectl -n ebro delete pod tmp --ignore-not-found >/dev/null 2>&1; sleep 3
|
|
done
|
|
echo "$IP" > {WS}/q56/ip.txt
|
|
""",
|
|
57: r"""
|
|
kubectl -n dnieper patch ingress portal --type=json -p '[
|
|
{"op": "replace", "path": "/spec/rules/0/http/paths/0/pathType", "value": "Prefix"},
|
|
{"op": "replace", "path": "/spec/rules/0/http/paths/0/backend/service/port/number", "value": 8080},
|
|
{"op": "replace", "path": "/spec/rules/0/http/paths/1/backend/service/name", "value": "portal-ui"}]'
|
|
""",
|
|
# ---- 58-64: bank_more.py; 60 is further down
|
|
58: r"""
|
|
cd {WS}/q58
|
|
sed -i 's#^apiVersion: extensions/v1beta1#apiVersion: apps/v1#' deployment.yaml
|
|
yq -i '.spec.selector.matchLabels.app = "%{app}"' deployment.yaml
|
|
kubectl apply -f deployment.yaml
|
|
kubectl apply -f flag.yaml --dry-run=server 2> warning.txt >/dev/null
|
|
sed -i 's#rollout.example.com/v1beta1#rollout.example.com/v1#' flag.yaml
|
|
kubectl apply -f flag.yaml
|
|
kubectl api-resources --no-headers | awk '{k=$NF; if (k=="CronJob" || k=="HorizontalPodAutoscaler" || k=="EndpointSlice" || k=="PriorityClass") print k, $(NF-2)}' > served.txt
|
|
kubectl -n %{ns} rollout status deploy %{app} --timeout=240s
|
|
""",
|
|
59: r"""
|
|
cd {WS}/q59
|
|
echo cc > shortname.txt
|
|
kubectl get cc -A --no-headers | awk '$NF == "Degraded" {print $1 "/" $2}' > degraded.txt
|
|
MAX=$(kubectl get crd cacheclusters.cache.example.com -o jsonpath='{.spec.versions[0].schema.openAPIV3Schema.properties.spec.properties.replicas.maximum}')
|
|
cat > cache.yaml <<EOF
|
|
apiVersion: cache.example.com/v1
|
|
kind: CacheCluster
|
|
metadata:
|
|
name: %{name}
|
|
namespace: %{ns}
|
|
spec:
|
|
engine: %{eng}
|
|
replicas: $MAX
|
|
memoryMb: %{mem}
|
|
EOF
|
|
kubectl apply -f cache.yaml
|
|
""",
|
|
61: r"""
|
|
cd {WS}/q61/app
|
|
sed -i 's/name: store-front/name: storefront/' replicas-patch.yaml
|
|
cat > kustomization.yaml <<EOF
|
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
|
kind: Kustomization
|
|
resources:
|
|
- deployment.yaml
|
|
- service.yaml
|
|
patches:
|
|
- path: replicas-patch.yaml
|
|
namespace: %{ns}
|
|
nameSuffix: -%{sfx}
|
|
images:
|
|
- {name: nginx, newTag: "1.27"}
|
|
secretGenerator:
|
|
- name: storefront-token
|
|
literals: [token=%{tok}]
|
|
generatorOptions:
|
|
disableNameSuffixHash: true
|
|
EOF
|
|
kubectl apply -k .
|
|
kubectl -n %{ns} rollout status deploy storefront-%{sfx} --timeout=240s
|
|
""",
|
|
62: r"""
|
|
echo Unhealthy > {WS}/q62/reason.txt
|
|
kubectl -n %{ns} patch deploy %{dep} --type=json -p '[
|
|
{"op": "replace", "path": "/spec/template/spec/containers/0/livenessProbe/httpGet/port", "value": %{port}},
|
|
{"op": "replace", "path": "/spec/template/spec/containers/0/readinessProbe/httpGet/path", "value": "/%{ready}"}]'
|
|
kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s
|
|
""",
|
|
63: r"""
|
|
echo "kubectl -n %{ns} get pods -o custom-columns=POD:.metadata.name,NODE:.spec.nodeName,IMAGE:.spec.containers[0].image" > {WS}/q63/inventory.sh
|
|
kubectl -n %{ns} get pods -l tier=%{tier} --field-selector='status.phase!=Running' -o name | cut -d/ -f2 > {WS}/q63/not-running.txt
|
|
kubectl -n %{ns} get pods -o jsonpath='{range .items[*]}{.spec.nodeName}{"\n"}{end}' | grep . | sort | uniq -c | sort -rn | awk 'NR==1 {print $2}' > {WS}/q63/busiest.txt
|
|
""",
|
|
64: r"""
|
|
for i in $(seq 1 30); do
|
|
B=$(kubectl -n %{ns} logs -l app=%{app} --prefix --tail=-1 2>/dev/null | grep ERROR | head -1 | sed 's#^\[pod/\([^/]*\)/.*#\1#')
|
|
[ -n "$B" ] && break; sleep 3
|
|
done
|
|
echo "$B" > {WS}/q64/bad-pod.txt
|
|
kubectl -n %{ns} logs "$B" | grep ERROR > {WS}/q64/errors.log
|
|
kubectl -n %{ns} logs %{app}-0 -c migrate | awk '/schema version/ {print $3}' > {WS}/q64/schema.txt
|
|
echo "kubectl -n %{ns} logs -l app=%{app} --prefix --tail=2" > {WS}/q64/tail.sh
|
|
""",
|
|
13: r"""
|
|
cd {WS}/q13/image
|
|
podman build -q -t localhost/%{img}:v1 . >/dev/null
|
|
sed -i 's/^ENV GREETING=hello/ENV GREETING=%{greet}\nENV APP_VERSION=v2/' Dockerfile
|
|
podman build -q -t localhost/%{img}:v2 . >/dev/null
|
|
podman save --format oci-archive -o {WS}/q13/%{img}-v2.tar localhost/%{img}:v2 >/dev/null 2>&1
|
|
podman run --rm localhost/%{img}:v2 > {WS}/q13/run.txt
|
|
""",
|
|
41: r"""
|
|
cd {WS}/q41/image
|
|
docker build -q --build-arg APP_VERSION=2.1 -t localhost/beacon-relay:2.1 -t localhost/beacon-relay:latest . >/dev/null
|
|
sudo podman run -d --name beacon-relay localhost/beacon-relay:2.1 >/dev/null
|
|
sleep 2; podman logs beacon-relay > {WS}/q41/relay.log
|
|
""",
|
|
60: r"""
|
|
cd {WS}/q60/image
|
|
printf 'FROM docker.io/library/busybox:1.36\nLABEL team=%{team}\nCOPY report.sh /app/report.sh\nRUN chmod 0755 /app/report.sh\nUSER 10001\nENTRYPOINT ["/app/report.sh"]\nCMD ["%{arg}"]\n' > Dockerfile
|
|
podman build -q -t localhost/%{img}:1.1 . >/dev/null
|
|
podman run --rm localhost/%{img}:1.1 > {WS}/q60/default.txt
|
|
podman run --rm localhost/%{img}:1.1 %{arg2} > {WS}/q60/%{arg2}.txt
|
|
podman rmi localhost/%{img}:1.0 >/dev/null
|
|
""",
|
|
# ---- 65-76: Services and Networking (bank_net.py). A policy needs a moment to reach the node, and
|
|
# ---- the audit grades the instant the script ends - hence the sleeps a person would not need.
|
|
65: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata: {name: default-deny-ingress, namespace: %{ns}}
|
|
spec:
|
|
podSelector: {}
|
|
policyTypes: [Ingress]
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata: {name: api-from-web, namespace: %{ns}}
|
|
spec:
|
|
podSelector: {matchLabels: {app: api}}
|
|
policyTypes: [Ingress]
|
|
ingress:
|
|
- from: [{podSelector: {matchLabels: {app: web}}}]
|
|
ports: [{protocol: TCP, port: 80}]
|
|
EOF
|
|
sleep 5
|
|
""",
|
|
66: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata: {name: metrics-scrape, namespace: %{ns}}
|
|
spec:
|
|
podSelector: {matchLabels: {app: metrics}}
|
|
policyTypes: [Ingress]
|
|
ingress:
|
|
- from:
|
|
- namespaceSelector: {matchLabels: {purpose: monitoring}}
|
|
podSelector: {matchLabels: {role: scraper}}
|
|
ports: [{protocol: TCP, port: 80}]
|
|
EOF
|
|
sleep 5
|
|
""",
|
|
67: r"""
|
|
kubectl -n %{ns} patch netpol cache-allow --type=json -p '[{"op": "replace", "path": "/spec/ingress/0/from/0/podSelector/matchLabels", "value": {"app": "worker"}}, {"op": "replace", "path": "/spec/ingress/0/ports/0/port", "value": 80}]'
|
|
sleep 5
|
|
""",
|
|
68: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata: {name: same-namespace, namespace: %{ns}}
|
|
spec:
|
|
podSelector: {}
|
|
policyTypes: [Ingress]
|
|
ingress:
|
|
- from: [{podSelector: {}}]
|
|
EOF
|
|
sleep 5
|
|
""",
|
|
69: r"""
|
|
kubectl -n %{ns} label pod reporter access=db
|
|
sleep 5
|
|
""",
|
|
70: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata: {name: %{app}, namespace: %{ns}}
|
|
spec:
|
|
selector: {app: %{app}}
|
|
sessionAffinity: ClientIP
|
|
ports:
|
|
- name: web
|
|
port: %{port}
|
|
targetPort: http-main
|
|
EOF
|
|
sleep 3
|
|
""",
|
|
71: r"""
|
|
kubectl -n %{ns} create service externalname inventory --external-name warehouse.%{ns}-data.svc.cluster.local
|
|
echo warehouse.%{ns}-data.svc.cluster.local > {WS}/q71/target.txt
|
|
""",
|
|
72: r"""
|
|
kubectl -n %{ns} describe pod -l app=%{app} | grep -i 'readiness probe failed' | head -1
|
|
echo 404 > {WS}/q72/code.txt
|
|
kubectl -n %{ns} patch deploy %{app} --type=json -p '[{"op": "replace", "path": "/spec/template/spec/containers/0/readinessProbe/httpGet/path", "value": "/"}]'
|
|
kubectl -n %{ns} rollout status deploy %{app} --timeout=180s
|
|
""",
|
|
73: r"""
|
|
IP=$(kubectl -n %{ns}-old get pod legacy -o jsonpath='{.status.podIP}')
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata: {name: legacy-gw, namespace: %{ns}}
|
|
spec:
|
|
ports: [{port: 80, targetPort: 80}]
|
|
---
|
|
apiVersion: discovery.k8s.io/v1
|
|
kind: EndpointSlice
|
|
metadata:
|
|
name: legacy-gw-1
|
|
namespace: %{ns}
|
|
labels: {kubernetes.io/service-name: legacy-gw}
|
|
addressType: IPv4
|
|
ports: [{port: 80, protocol: TCP}]
|
|
endpoints: [{addresses: ["$IP"]}]
|
|
EOF
|
|
sleep 3
|
|
""",
|
|
74: r"""
|
|
kubectl -n %{ns} set selector svc %{app} app=%{app},version=v2
|
|
sleep 4
|
|
kubectl -n %{ns} exec probe -- wget -T 3 -qO- http://%{app} > {WS}/q74/response.txt
|
|
""",
|
|
75: r"""
|
|
kubectl -n %{ns} create ingress %{dom} --class=nginx --rule="shop.%{dom}.test/*=shop:80" --rule="blog.%{dom}.test/*=blog:8080" --default-backend=fallback:80
|
|
""",
|
|
76: r"""
|
|
kubectl -n %{ns} create ingress %{dom}-routes --class=nginx --rule="%{dom}.example.com/healthz=status:8080" --rule="%{dom}.example.com/app*=app:80" --annotation nginx.ingress.kubernetes.io/rewrite-target=/
|
|
""",
|
|
# ---- 77-84: Application Design and Build (bank_design.py)
|
|
77: r"""
|
|
cd {WS}/q77/image
|
|
printf 'FROM busybox:1.36\nWORKDIR /app\nCOPY run.sh /app/run.sh\nRUN chmod +x /app/run.sh\nENTRYPOINT ["/app/run.sh"]\n' > Dockerfile
|
|
podman build -q -t localhost/%{img}:1.0 . >/dev/null
|
|
podman tag localhost/%{img}:1.0 localhost/%{img}:stable
|
|
podman run --rm localhost/%{img}:1.0 > {WS}/q77/out.txt
|
|
""",
|
|
78: r"""
|
|
podman image inspect localhost/%{img}:%{tag} --format 'user={{.Config.User}}
|
|
workdir={{.Config.WorkingDir}}
|
|
tier={{index .Config.Labels "tier"}}' > {WS}/q78/facts.txt
|
|
echo "port=$(podman image inspect localhost/%{img}:%{tag} --format '{{range $p, $v := .Config.ExposedPorts}}{{$p}}{{end}}' | cut -d/ -f1)" >> {WS}/q78/facts.txt
|
|
podman run --rm -e MODE=debug localhost/%{img}:%{tag} check > {WS}/q78/run.txt
|
|
""",
|
|
79: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata: {name: %{app}, namespace: %{ns}}
|
|
spec:
|
|
clusterIP: None
|
|
selector: {app: %{app}}
|
|
ports: [{port: 80}]
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: StatefulSet
|
|
metadata: {name: %{app}, namespace: %{ns}}
|
|
spec:
|
|
serviceName: %{app}
|
|
replicas: 2
|
|
selector: {matchLabels: {app: %{app}}}
|
|
template:
|
|
metadata: {labels: {app: %{app}}}
|
|
spec:
|
|
containers:
|
|
- name: store
|
|
image: busybox:1.36
|
|
command: ["sh", "-c", "hostname >> /data/started; exec httpd -f -p 80 -h /data"]
|
|
volumeMounts: [{name: data, mountPath: /data}]
|
|
volumeClaimTemplates:
|
|
- metadata: {name: data}
|
|
spec:
|
|
accessModes: [ReadWriteOnce]
|
|
storageClassName: standard
|
|
resources: {requests: {storage: 50Mi}}
|
|
EOF
|
|
kubectl -n %{ns} rollout status sts %{app} --timeout=240s
|
|
kubectl -n %{ns} delete pod %{app}-0
|
|
sleep 3
|
|
kubectl -n %{ns} rollout status sts %{app} --timeout=240s
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{app}-0 --timeout=120s
|
|
""",
|
|
80: r"""
|
|
cat <<'EOF' | kubectl apply -f -
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata: {name: %{job}, namespace: %{ns}}
|
|
spec:
|
|
backoffLimit: 4
|
|
activeDeadlineSeconds: 120
|
|
ttlSecondsAfterFinished: 600
|
|
template:
|
|
spec:
|
|
restartPolicy: OnFailure
|
|
volumes: [{name: work, emptyDir: {}}]
|
|
containers:
|
|
- name: import
|
|
image: busybox:1.36
|
|
command: ["sh", "-c", "if [ -f /work/tried ]; then echo imported; else touch /work/tried; echo failing once; exit 1; fi"]
|
|
volumeMounts: [{name: work, mountPath: /work}]
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Complete job/%{job} --timeout=120s
|
|
""",
|
|
81: r"""
|
|
cat <<'EOF' | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: %{pod}, namespace: %{ns}}
|
|
spec:
|
|
volumes:
|
|
- name: conf
|
|
configMap: {name: ambassador-conf}
|
|
containers:
|
|
- name: app
|
|
image: busybox:1.36
|
|
command: ["sh", "-c", "while true; do wget -T 2 -qO- http://localhost:8080/ > /tmp/last 2>&1; sleep 5; done"]
|
|
- name: ambassador
|
|
image: nginx:1.27
|
|
volumeMounts: [{name: conf, mountPath: /etc/nginx/conf.d}]
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
sleep 8
|
|
""",
|
|
82: r"""
|
|
kubectl -n %{ns} get pod %{pod} -o json | jq 'del(.status, .metadata.uid, .metadata.resourceVersion, .metadata.creationTimestamp) | .spec.initContainers |= reverse | .spec.containers[0].volumeMounts += [{"name": "work", "mountPath": "/work"}]' > /tmp/pod.json
|
|
kubectl replace --force -f /tmp/pod.json
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
""",
|
|
83: r"""
|
|
kubectl -n %{ns} delete pvc %{pvc}
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: PersistentVolumeClaim
|
|
metadata: {name: %{pvc}, namespace: %{ns}}
|
|
spec:
|
|
accessModes: [ReadWriteOnce]
|
|
storageClassName: manual
|
|
resources: {requests: {storage: 1Gi}}
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
""",
|
|
84: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: %{pod}, namespace: %{ns}}
|
|
spec:
|
|
volumes:
|
|
- name: status
|
|
configMap: {name: %{cm}}
|
|
containers:
|
|
- name: nginx
|
|
image: nginx:1.27
|
|
volumeMounts:
|
|
- name: status
|
|
mountPath: /etc/nginx/conf.d/status.conf
|
|
subPath: status.conf
|
|
readOnly: true
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
""",
|
|
# ---- 85-93: Application Deployment (bank_deploy.py)
|
|
85: r"""
|
|
kubectl -n %{ns} get deploy %{app}-canary -o jsonpath='{.spec.template.spec.containers[0].image}' > {WS}/q85/image.txt
|
|
kubectl -n %{ns} set image deploy/%{app}-stable web="$(cat {WS}/q85/image.txt)"
|
|
kubectl -n %{ns} rollout status deploy/%{app}-stable --timeout=240s
|
|
kubectl -n %{ns} delete deploy %{app}-canary
|
|
sleep 20
|
|
""",
|
|
86: r"""
|
|
kubectl -n %{ns} patch deploy %{dep} --type=json -p '[{"op": "replace", "path": "/spec/strategy", "value": {"type": "Recreate"}}]'
|
|
kubectl -n %{ns} set image deploy/%{dep} nginx=nginx:1.27
|
|
kubectl -n %{ns} rollout status deploy/%{dep} --timeout=240s
|
|
echo Recreate > {WS}/q86/strategy.txt
|
|
""",
|
|
87: r"""
|
|
kubectl -n %{ns} rollout pause deploy/%{dep}
|
|
kubectl -n %{ns} set image deploy/%{dep} nginx=nginx:1.27
|
|
kubectl -n %{ns} set env deploy/%{dep} LOG_LEVEL=debug
|
|
kubectl -n %{ns} set resources deploy/%{dep} --requests=cpu=50m,memory=64Mi
|
|
kubectl -n %{ns} rollout resume deploy/%{dep}
|
|
kubectl -n %{ns} rollout status deploy/%{dep} --timeout=240s
|
|
sleep 10
|
|
""",
|
|
88: r"""
|
|
for i in 1 2 3 4; do kubectl -n %{ns} rollout history deploy/%{dep} --revision=$i | grep -q 'busybox:%{tag}' && echo $i; done > {WS}/q88/revision.txt
|
|
kubectl -n %{ns} rollout undo deploy/%{dep} --to-revision=$(cat {WS}/q88/revision.txt)
|
|
kubectl -n %{ns} annotate deploy %{dep} kubernetes.io/change-cause="rollback to busybox:%{tag}" --overwrite
|
|
kubectl -n %{ns} rollout status deploy/%{dep} --timeout=240s
|
|
sleep 35
|
|
""",
|
|
89: r"""
|
|
kubectl -n %{ns} rollout restart deploy/%{app}
|
|
kubectl -n %{ns} rollout status deploy/%{app} --timeout=240s
|
|
kubectl -n %{ns} annotate deploy %{app} kubernetes.io/change-cause="secret rotation" --overwrite
|
|
sleep 35
|
|
kubectl -n %{ns} logs deploy/%{app} | head -1 > {WS}/q89/started.txt
|
|
""",
|
|
90: r"""
|
|
cd {WS}/q90
|
|
NS=$(helm list -A -o json | jq -r '.[] | select(.name == "%{rel}") | .namespace')
|
|
echo $NS > namespace.txt
|
|
helm list -A -o json | jq -r '.[] | select(.name == "%{rel}") | .chart' > chart.txt
|
|
helm -n $NS get values %{rel} -o yaml > values.yaml
|
|
helm -n $NS get manifest %{rel} > manifest.yaml
|
|
""",
|
|
91: r"""
|
|
helm repo add internal http://localhost:30080 >/dev/null
|
|
helm repo update >/dev/null
|
|
helm search repo internal/webapp --versions -o json | jq -r '.[].version' > {WS}/q91/versions.txt
|
|
helm install %{rel} internal/webapp --version 1.2.0 -n %{ns} --create-namespace --set replicaCount=2 >/dev/null
|
|
helm upgrade %{rel} internal/webapp -n %{ns} --reuse-values >/dev/null
|
|
""",
|
|
92: r"""
|
|
echo image.tag > {WS}/q92/key.txt
|
|
helm -n %{ns} upgrade %{rel} {WS}/q92/chart --reuse-values --set image.tag=1.27 --wait --timeout 180s >/dev/null
|
|
helm -n %{ns}-archive uninstall %{rel}-old >/dev/null
|
|
sleep 10
|
|
""",
|
|
93: r"""
|
|
cd {WS}/q93/app
|
|
printf 'apiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\nnamespace: %{ns}\nresources:\n - deployment.yaml\nconfigMapGenerator:\n - name: %{app}-config\n envs:\n - settings.env\n' > kustomization.yaml
|
|
kubectl apply -k .
|
|
kubectl -n %{ns} rollout status deploy/%{app} --timeout=240s
|
|
sed -i 's/^MODE=blue/MODE=green/' settings.env
|
|
kubectl apply -k .
|
|
kubectl -n %{ns} rollout status deploy/%{app} --timeout=240s
|
|
sleep 35
|
|
""",
|
|
# ---- 94-99: Application Observability and Maintenance (bank_obs.py)
|
|
94: r"""
|
|
kubectl -n %{ns} patch deploy %{app} --type=json -p '[{"op": "add", "path": "/spec/template/spec/containers/0/readinessProbe", "value": {"httpGet": {"path": "/ready", "port": 80}, "periodSeconds": 3, "failureThreshold": 1}}]'
|
|
kubectl -n %{ns} rollout status deploy/%{app} --timeout=240s
|
|
sleep 35
|
|
POD=$(kubectl -n %{ns} get pod -l app=%{app} --field-selector status.phase=Running -o jsonpath='{.items[0].metadata.name}')
|
|
kubectl -n %{ns} exec $POD -- rm /www/ready
|
|
echo $POD > {WS}/q94/unready.txt
|
|
sleep 10
|
|
""",
|
|
95: r"""
|
|
kubectl -n %{ns} patch deploy %{dep} --type=json -p '[{"op": "add", "path": "/spec/template/spec/containers/0/startupProbe", "value": {"httpGet": {"path": "/", "port": 80}, "periodSeconds": 5, "failureThreshold": 18}}]'
|
|
kubectl -n %{ns} rollout status deploy/%{dep} --timeout=240s
|
|
""",
|
|
96: r"""
|
|
echo FailedScheduling > {WS}/q96/reason.txt
|
|
kubectl -n %{ns} get events --field-selector reason=FailedScheduling -o jsonpath='{.items[-1:].message}' > {WS}/q96/message.txt
|
|
kubectl -n %{ns} set resources deploy/%{dep} --requests=cpu=50m,memory=640Mi --limits=memory=640Mi
|
|
kubectl -n %{ns} rollout status deploy/%{dep} --timeout=240s
|
|
""",
|
|
97: r"""
|
|
kubectl -n %{ns} logs -l job-name=%{job} --tail=-1 | grep '^result' > {WS}/q97/results.txt
|
|
kubectl -n %{ns} logs job/%{job}-load | grep '^FATAL' > {WS}/q97/fatal.txt
|
|
kubectl -n %{ns} logs job/%{job}-load | head -1 > {WS}/q97/first.txt
|
|
""",
|
|
98: r"""
|
|
kubectl -n %{ns} logs deploy/%{dep} -c migrate > {WS}/q98/why.log 2>&1
|
|
kubectl -n %{ns} patch cm %{dep}-config -p '{"data": {"DB_HOST": "db.%{ns}.svc"}}'
|
|
kubectl -n %{ns} rollout restart deploy/%{dep}
|
|
kubectl -n %{ns} rollout status deploy/%{dep} --timeout=240s
|
|
sleep 5
|
|
""",
|
|
99: r"""
|
|
kubectl -n %{ns} debug %{pod} --image=busybox:1.36 --target=whoami --container=dbg -- sh -c 'tr "\0" "\n" < /proc/1/environ | grep WHOAMI_PORT_NUMBER; sleep 3600' >/dev/null 2>&1
|
|
for i in $(seq 1 20); do P=$(kubectl -n %{ns} logs %{pod} -c dbg 2>/dev/null | sed -n 's/^WHOAMI_PORT_NUMBER=//p'); [ -n "$P" ] && break; sleep 2; done
|
|
echo $P > {WS}/q99/port.txt
|
|
kubectl -n %{ns} patch svc %{pod} -p "{\"spec\": {\"ports\": [{\"port\": 80, \"targetPort\": $P}]}}"
|
|
sleep 5
|
|
""",
|
|
# ---- 100-105: Application Environment, Configuration and Security (bank_env.py)
|
|
100: r"""
|
|
kubectl -n %{ns} create rolebinding %{dev}-view --clusterrole=view --user=%{dev}
|
|
kubectl -n %{ns} create role pod-restarter --verb=delete --resource=pods
|
|
kubectl -n %{ns} create rolebinding %{dev}-restart --role=pod-restarter --user=%{dev}
|
|
kubectl -n %{ns} create rolebinding %{lead}-edit --clusterrole=edit --user=%{lead}
|
|
for q in "get pods --subresource=log --as=%{dev}" "get secrets --as=%{dev}" "create rolebindings --as=%{lead}"; do kubectl -n %{ns} auth can-i $q; done > {WS}/q100/answers.txt 2>/dev/null
|
|
""",
|
|
101: r"""
|
|
echo requests.cpu > {WS}/q101/resource.txt
|
|
kubectl -n %{ns} set resources deploy/%{dep} --requests=cpu=200m,memory=64Mi
|
|
kubectl -n %{ns} rollout status deploy/%{dep} --timeout=240s
|
|
sleep 5
|
|
""",
|
|
102: r"""
|
|
cd {WS}/q102
|
|
kubectl -n %{ns} create configmap %{cm}-env --from-env-file=logging.env
|
|
kubectl -n %{ns} create configmap %{cm}-files --from-file=application.properties=app.properties
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: %{pod}, namespace: %{ns}}
|
|
spec:
|
|
volumes:
|
|
- name: files
|
|
configMap:
|
|
name: %{cm}-files
|
|
defaultMode: 0440
|
|
items:
|
|
- {key: application.properties, path: conf/app.properties}
|
|
containers:
|
|
- name: sender
|
|
image: busybox:1.36
|
|
command: [sleep, "86400"]
|
|
envFrom:
|
|
- prefix: LOG_
|
|
configMapRef: {name: %{cm}-env}
|
|
volumeMounts: [{name: files, mountPath: /etc/app}]
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
""",
|
|
103: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: %{pod}, namespace: %{ns}}
|
|
spec:
|
|
securityContext:
|
|
runAsUser: %{uid}
|
|
runAsGroup: %{gid}
|
|
fsGroup: %{fsg}
|
|
volumes: [{name: work, emptyDir: {}}]
|
|
containers:
|
|
- name: scanner
|
|
image: busybox:1.36
|
|
command: [sleep, "86400"]
|
|
securityContext:
|
|
readOnlyRootFilesystem: true
|
|
allowPrivilegeEscalation: false
|
|
capabilities: {drop: [ALL]}
|
|
volumeMounts: [{name: work, mountPath: /work}]
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
kubectl -n %{ns} exec %{pod} -- id > {WS}/q103/id.txt
|
|
kubectl -n %{ns} exec %{pod} -- touch /work/proof
|
|
""",
|
|
104: r"""
|
|
kubectl -n %{ns} create sa %{dep}
|
|
kubectl -n %{ns} create role %{dep} --verb=get --resource=configmaps --resource-name=feature-flags
|
|
kubectl -n %{ns} create rolebinding %{dep} --role=%{dep} --serviceaccount=%{ns}:%{dep}
|
|
kubectl -n %{ns} set serviceaccount deploy/%{dep} %{dep}
|
|
kubectl -n %{ns} rollout status deploy/%{dep} --timeout=240s
|
|
sleep 12
|
|
""",
|
|
105: r"""
|
|
cat <<EOF | kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata: {name: %{pod}, namespace: %{ns}}
|
|
spec:
|
|
volumes:
|
|
- name: ca
|
|
secret:
|
|
secretName: %{sec}-ca
|
|
items:
|
|
- {key: ca.crt, path: ca.crt}
|
|
containers:
|
|
- name: client
|
|
image: busybox:1.36
|
|
command: [sleep, "86400"]
|
|
envFrom:
|
|
- prefix: DB_
|
|
secretRef: {name: %{sec}}
|
|
volumeMounts: [{name: ca, mountPath: /etc/ssl/db, readOnly: true}]
|
|
EOF
|
|
kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=180s
|
|
""",
|
|
}
|