"""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. A command that reads stdin (ssh, kubectl run -i) would swallow the rest of the script: ssh -n, bash """ # cpwait COMPONENT: the control-plane component's static Pod on this host is ready (again) CPWAIT = r""" cpwait() { local i for i in $(seq 1 80); do [ "$(kubectl -n kube-system get pod $1-{CP} -o jsonpath='{.status.containerStatuses[0].ready}' 2>/dev/null)" = true ] && return 0 sleep 3 done return 1 } """ SOLVE = { # ---- 114-124: bank_storage.py 114: r""" cat < /data/keep.txt; sleep 86400"] volumeMounts: [{name: data, mountPath: /data}] EOF kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=240s kubectl -n %{ns} get pvc %{pvc} -o jsonpath='{.spec.volumeName}' > {WS}/q114/pv.txt kubectl -n %{ns} delete pod %{pod} kubectl -n %{ns} delete pvc %{pvc} sleep 4 kubectl get pv $(cat {WS}/q114/pv.txt) -o jsonpath='{.status.phase}' > {WS}/q114/phase.txt """, 115: r""" cat < {WS}/q115/claim.txt kubectl -n %{ns} get pvc %{pod}-scratch -o jsonpath='{.metadata.ownerReferences[0].kind}/{.metadata.ownerReferences[0].name}' > {WS}/q115/owner.txt """, 116: r""" kubectl -n %{ns} patch sts %{sts} -p '{"spec":{"persistentVolumeClaimRetentionPolicy":{"whenScaled":"Delete","whenDeleted":"Retain"}}}' kubectl -n %{ns} scale sts %{sts} --replicas=1 for i in $(seq 1 40); do [ "$(kubectl -n %{ns} get pvc --no-headers | wc -l)" = 1 ] && break; sleep 3; done kubectl -n %{ns} get pvc -o name | cut -d/ -f2 > {WS}/q116/claims.txt """, 117: r""" cat < {WS}/q118/wrong-pv.txt kubectl -n %{ns} scale deploy %{dep} --replicas=0 kubectl -n %{ns} wait --for=delete pod -l app=%{dep} --timeout=120s kubectl -n %{ns} delete pvc %{pvc} cat < {WS}/q119/reasons.txt kubectl -n %{ns} delete pvc reports archive media for c in "reports 1Gi" "archive 2Gi" "media 1Gi"; do set -- $c; cat < {WS}/q120/event.txt kubectl -n %{ns} patch deploy %{dep} --type=json -p '[{"op":"remove","path":"/spec/template/spec/nodeSelector"}]' kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s """, 121: r""" kubectl -n %{ns} get pvc %{pvc} -o jsonpath='{.metadata.finalizers[0]}' > {WS}/q121/finalizer.txt echo deployment/%{dep} > {WS}/q121/used-by.txt kubectl -n %{ns} patch deploy %{dep} --type=json -p '[{"op":"replace","path":"/spec/template/spec/volumes/0","value":{"name":"files","emptyDir":{}}}]' kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s """, 122: r""" kubectl -n local-path-storage scale deploy local-path-provisioner --replicas=1 kubectl get sc %{sc} -o json | jq 'del(.metadata.uid, .metadata.resourceVersion, .metadata.creationTimestamp, .metadata.managedFields, .metadata.annotations) | .provisioner = "rancher.io/local-path"' > /tmp/sc.json kubectl delete sc %{sc} kubectl apply -f /tmp/sc.json for i in $(seq 1 40); do [ "$(kubectl -n %{ns} get pvc %{pvc} -o jsonpath='{.status.phase}')" = Bound ] && break; sleep 3; done kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=240s echo rancher.io/local-path > {WS}/q122/provisioner.txt """, 123: r""" kubectl annotate sc %{sc} storageclass.kubernetes.io/is-default-class=true for i in $(seq 1 40); do [ "$(kubectl -n %{ns} get pvc %{pvc} -o jsonpath='{.status.phase}')" = Bound ] && break; sleep 3; done kubectl -n %{ns} wait --for=condition=Ready pod/%{pod} --timeout=240s kubectl -n %{ns} get pvc %{pvc} -o jsonpath='{.spec.storageClassName}' > {WS}/q123/class.txt """, 124: r""" cat < {WS}/q125/coredns-backup.yaml ADD=$' hosts {\n %{ip} %{host}.dc.internal\n fallthrough\n }\n rewrite name %{svc}.dc.internal %{svc}.%{ns}.svc.cluster.local\n' kubectl -n kube-system get cm coredns -o json | jq --arg add "$ADD" '.data.Corefile |= sub(" kubernetes "; $add + " kubernetes ")' | kubectl apply -f - kubectl -n kube-system rollout restart deploy coredns kubectl -n kube-system rollout status deploy coredns --timeout=240s """, 126: r""" cat < {WS}/q128/ips.txt sleep 8 """, # ---- 129-131: bank_tshoot.py 129: r""" ssh -n {W1} 'sudo systemctl enable --now containerd; echo "runtime-endpoint: unix:///run/containerd/containerd.sock" | sudo tee /etc/crictl.yaml >/dev/null' echo containerd > {WS}/q129/unit.txt for i in $(seq 1 40); do [ "$(kubectl get node {W1} -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')" = True ] && break; sleep 3; done """, 130: r""" for i in $(seq 1 60); do U=$(kubectl -n %{ns} top pod query --no-headers 2>/dev/null | awk '{gsub("Mi","",$3); print $3}') [ "${U:-0}" -ge 50 ] 2>/dev/null && [ "$(kubectl -n %{ns} top pod --no-headers 2>/dev/null | wc -l)" -ge 3 ] && break sleep 5 done kubectl -n %{ns} top pod query --containers --no-headers | awk '{gsub("Mi","",$4); print $4, $2}' | sort -rn | head -1 | awk '{print $2}' > {WS}/q130/container.txt for p in ingest index query; do use=$(kubectl -n %{ns} top pod $p --no-headers | awk '{gsub("Mi","",$3); print $3}') req=$(kubectl -n %{ns} get pod $p -o json | jq '[.spec.containers[].resources.requests.memory | sub("Mi"; "") | tonumber] | add') [ "$use" -gt "$req" ] && echo $p done > {WS}/q130/over.txt echo 'kubectl -n %{ns} top pod --containers' > {WS}/q130/top.sh """, 131: r""" kubectl -n %{ns} logs %{pod} -c api --previous > {WS}/q131/previous.log U=$(kubectl -n %{ns} get pod %{pod} -o jsonpath='{.metadata.uid}') N=$(kubectl -n %{ns} get pod %{pod} -o jsonpath='{.status.containerStatuses[?(@.name=="api")].restartCount}') NODE=$(kubectl -n %{ns} get pod %{pod} -o jsonpath='{.spec.nodeName}') F=/var/log/pods/%{ns}_%{pod}_$U/api/$N.log echo $F > {WS}/q131/logfile.txt sleep 20 ssh -n $NODE "sudo grep ' stderr F ' $F" | cut -d' ' -f4- > {WS}/q131/stderr.log echo 'kubectl -n %{ns} logs %{pod} --all-containers --prefix --timestamps' > {WS}/q131/all.sh """, # ---- 1-113, where the reference solution is not a script as it stands: it opens a shell on a # ---- node, edits in vi, or leaves a value to be read off the screen. The same steps, scripted. # ---- (The host is the control-plane node: what a solution does after "ssh " is sudo here.) 2: r""" sudo mkdir -p /opt/cka sudo ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key snapshot save /opt/cka/etcd-snapshot.db """, 5: r""" ssh -n {W3} 'sudo tee /etc/kubernetes/manifests/static-web.yaml >/dev/null </dev/null)" = Running ] && break; sleep 3; done """, 17: r""" ssh -n {W2} 'sudo systemctl enable --now kubelet' for i in $(seq 1 40); do [ "$(kubectl get node {W2} -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')" = True ] && break; sleep 3; done """, 18: r""" kubectl -n troubleshoot set image deploy payments payments=nginx:1.27 kubectl -n troubleshoot patch svc payments -p '{"spec":{"selector":{"app":"payments"},"ports":[{"port":80,"targetPort":80}]}}' kubectl -n troubleshoot rollout status deploy payments --timeout=240s """, 20: CPWAIT + r""" sudo sed -i 's/kube-schedulerr/kube-scheduler/' /etc/kubernetes/manifests/kube-scheduler.yaml cpwait kube-scheduler """, 23: r""" kubectl -n kube-system patch deploy metrics-server --type=json -p '[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value": "--kubelet-insecure-tls"}]' kubectl -n kube-system rollout status deploy metrics-server --timeout=240s for i in $(seq 1 40); do kubectl top nodes >/dev/null 2>&1 && break; sleep 5; done kubectl -n %{ns} autoscale deploy %{dep} --min=2 --max=%{max} --cpu-percent=%{cpu} --name=%{dep} """, 37: r""" kubectl -n kube-system get cm coredns -o json | jq '.data.Corefile |= sub("forward \\. [0-9.]+"; "forward . /etc/resolv.conf")' | kubectl apply -f - kubectl -n kube-system rollout restart deploy coredns kubectl -n kube-system rollout status deploy coredns --timeout=240s """, 38: CPWAIT + r""" sudo sed -i '/--leader-elect-lease-duration=1s/d' /etc/kubernetes/manifests/kube-controller-manager.yaml cpwait kube-controller-manager """, 39: r""" ssh -n {W1} "sudo sed -i 's|ca-bundle.crt|ca.crt|' /var/lib/kubelet/config.yaml; sudo systemctl restart kubelet; sudo systemctl enable kubelet" for i in $(seq 1 40); do [ "$(kubectl get node {W1} -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')" = True ] && break; sleep 3; done """, 45: CPWAIT + r""" sudo etcdutl snapshot restore /opt/cka/backup.db --data-dir /var/lib/etcd-restore sudo sed -i 's|path: /var/lib/etcd$|path: /var/lib/etcd-restore|' /etc/kubernetes/manifests/etcd.yaml sleep 30 for i in $(seq 1 80); do kubectl -n default get cm %{cm} >/dev/null 2>&1 && break; sleep 3; done for c in etcd kube-apiserver kube-scheduler kube-controller-manager; do cpwait $c; done """, 46: r""" N=$(kubectl get nodes -l tier=edge -o jsonpath='{.items[0].metadata.name}') D=$(ssh -n $N "sudo awk '/^staticPodPath:/ {print \$2}' /var/lib/kubelet/config.yaml") kubectl create ns %{ns} 2>/dev/null ssh -n $N "sudo tee $D/%{pod}.yaml >/dev/null </dev/null)" = Running ] && break; sleep 3; done """, 51: r""" for r in $(kubectl -n %{ns} rollout history deploy %{dep} | awk '/^[0-9]/{print $1}'); do kubectl -n %{ns} rollout history deploy %{dep} --revision=$r | grep -q 'nginx:1.25' && N=$r done kubectl -n %{ns} rollout undo deploy %{dep} --to-revision=$N kubectl -n %{ns} scale deploy %{dep} --replicas=3 kubectl -n %{ns} rollout status deploy %{dep} --timeout=240s kubectl -n %{ns} rollout pause deploy %{dep} kubectl -n %{ns} set image deploy/%{dep} $(kubectl -n %{ns} get deploy %{dep} -o jsonpath='{.spec.template.spec.containers[0].name}')=nginx:1.27 kubectl -n %{ns} set env deploy/%{dep} RELEASE=next """, 57: r""" kubectl -n kube-system delete netpol maintenance-lockdown kubectl -n kube-system scale deploy coredns --replicas=2 kubectl -n kube-system get cm coredns -o json | jq '.data.Corefile |= sub("forward \\. [0-9.]+"; "forward . /etc/resolv.conf")' | kubectl apply -f - kubectl -n kube-system rollout restart deploy coredns kubectl -n kube-system rollout status deploy coredns --timeout=240s """, 60: r""" ssh -n {W2} "sudo sed -i 's|ca-wrong.crt|ca.crt|' /var/lib/kubelet/config.yaml; sudo systemctl enable --now kubelet" kubectl uncordon {W2} for i in $(seq 1 40); do [ "$(kubectl get node {W2} -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')" = True ] && break; sleep 3; done """, 61: CPWAIT + r""" sudo sed -i 's/kube-schedulerr/kube-scheduler/' /etc/kubernetes/manifests/kube-scheduler.yaml sudo sed -i 's|\(image: registry.k8s.io/kube-controller-manager:v[0-9.]*\)-broken|\1|' /etc/kubernetes/manifests/kube-controller-manager.yaml cpwait kube-scheduler; cpwait kube-controller-manager """, 65: CPWAIT + r""" sudo kubeadm certs renew all sudo sh -c 'mkdir -p /tmp/manifests && mv /etc/kubernetes/manifests/*.yaml /tmp/manifests/ && sleep 25 && mv /tmp/manifests/*.yaml /etc/kubernetes/manifests/' for i in $(seq 1 80); do kubectl get nodes >/dev/null 2>&1 && break; sleep 3; done for c in etcd kube-apiserver kube-scheduler kube-controller-manager; do cpwait $c; done sudo kubeadm certs check-expiration 2>/dev/null | grep '^apiserver ' > {WS}/q65/expiry.txt """, 66: CPWAIT + r""" sudo sed -i 's/kube-apiserverr/kube-apiserver/' /etc/kubernetes/manifests/kube-apiserver.yaml for i in $(seq 1 80); do kubectl get nodes >/dev/null 2>&1 && break; sleep 3; done for c in etcd kube-apiserver kube-scheduler kube-controller-manager; do cpwait $c; done """, 67: r""" T=$(sudo kubeadm token create --ttl 2h) H=$(sudo cat /etc/kubernetes/pki/ca.crt | openssl x509 -pubkey | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //') E=$(kubectl get endpointslices -l kubernetes.io/service-name=kubernetes -o jsonpath='{.items[0].endpoints[0].addresses[0]}:{.items[0].ports[0].port}') printf 'JOIN_TOKEN=%s\nCA_HASH=sha256:%s\nAPI_ENDPOINT=%s\n' "$T" "$H" "$E" > {WS}/q67/node-join.env """, 70: r""" kubectl -n driver-onboard set image deploy/onboard-app onboard=nginx:1.25 kubectl -n driver-onboard patch deploy onboard-app --type=json -p '[ {"op": "replace", "path": "/spec/template/spec/containers/0/livenessProbe/httpGet/port", "value": 80}, {"op": "replace", "path": "/spec/template/spec/containers/0/readinessProbe/httpGet/path", "value": "/"}]' kubectl -n driver-onboard rollout status deploy onboard-app --timeout=240s """, 71: r""" kubectl -n dispatch-hub expose deploy dispatch-api --name=dispatch-service --port=80 --target-port=80 kubectl -n dispatch-hub rollout status deploy dispatch-api --timeout=240s for i in 1 2 3 4 5; do kubectl -n dispatch-hub run probe --image=busybox:1.36 --restart=Never -i --rm -- wget -qO- http://dispatch-service.dispatch-hub.svc:80 > {WS}/q71/test-result.txt /dev/null grep -qi nginx {WS}/q71/test-result.txt && break; kubectl -n dispatch-hub delete pod probe --ignore-not-found >/dev/null 2>&1; sleep 3 done kubectl -n dispatch-hub get svc dispatch-service -o jsonpath='{.spec.clusterIP}' > {WS}/q71/service-ip.txt """, 76: r""" kubectl -n trip-tracker get deploy trip-logger -o json | jq ' .spec.template.spec.volumes += [{"name": "config-volume", "emptyDir": {}}] | .spec.template.spec.initContainers += [{"name": "config-init", "image": "busybox:1.36", "command": ["sh", "-c", "echo '\''{\"region\":\"us-east\",\"mode\":\"production\"}'\'' > /config/app-config.json"], "volumeMounts": [{"name": "config-volume", "mountPath": "/config"}]}] | .spec.template.spec.containers[0].volumeMounts += [{"name": "config-volume", "mountPath": "/config"}]' | kubectl apply -f - kubectl -n trip-tracker rollout status deploy trip-logger --timeout=240s """, 79: r""" kubectl -n surge-pricing create cm surge-config --from-literal=PRICING_MODE=dynamic --from-literal=MAX_MULTIPLIER=3.5 --from-literal=REGION=us-east-1 kubectl -n surge-pricing create secret generic surge-db-secret --from-literal=DB_HOST=pricing-db.surge-pricing.svc.cluster.local \ --from-literal=DB_USER=surge_admin --from-literal=DB_PASSWORD='S3cur3Pr1c1ng!' kubectl -n surge-pricing get deploy surge-engine -o json | jq ' .spec.template.spec.containers[0].envFrom += [{"configMapRef": {"name": "surge-config"}}, {"secretRef": {"name": "surge-db-secret"}}] | .spec.template.spec.containers[0].volumeMounts += [{"name": "config-vol", "mountPath": "/etc/surge-config"}] | .spec.template.spec.volumes += [{"name": "config-vol", "configMap": {"name": "surge-config"}}]' | kubectl apply -f - kubectl -n surge-pricing rollout status deploy surge-engine --timeout=240s """, 81: r""" kubectl -n fare-engine delete pvc fare-data-pvc cat < {WS}/q84/ssl-line.txt """, 86: r""" cat > {WS}/q86/controlplane.txt <<'EOF' kubelet: process kube-apiserver: static-pod kube-scheduler: static-pod kube-controller-manager: static-pod etcd: static-pod dns: deployment coredns EOF """, 87: r""" C=$(kubectl get nodes --no-headers -l node-role.kubernetes.io/control-plane | wc -l) W=$(kubectl get nodes --no-headers -l '!node-role.kubernetes.io/control-plane' | wc -l) CIDR=$(kubectl -n kube-system get pod -l component=kube-apiserver -o yaml | grep -o 'service-cluster-ip-range=[^ ]*' | head -1 | cut -d= -f2) F=$(ssh -n {W1} "sudo sh -c 'ls /etc/cni/net.d/*.conflist | sort | head -1'") printf '1: %s\n2: %s\n3: %s\n4: kindnet, %s\n5: -{W3}\n' "$C" "$W" "$CIDR" "$F" > {WS}/q87/cluster-info.txt """, 88: r""" kubectl -n oslo-web run gatekeeper --image=busybox:1.36 --dry-run=client -o json -- sleep 86400 | jq '.spec.containers[0].readinessProbe = {"exec": {"command": ["sh", "-c", "wget -T2 -qO- http://ledger-api:8080"]}, "initialDelaySeconds": 5, "periodSeconds": 5}' | kubectl apply -f - sleep 12 kubectl -n oslo-web expose deploy ledger --name=ledger-api --port=8080 --target-port=80 kubectl -n oslo-web wait --for=condition=Ready pod/gatekeeper --timeout=180s """, 90: r""" cat > {WS}/q90/cluster-events.sh <<'EOF' kubectl get events -A --sort-by=.metadata.creationTimestamp EOF chmod +x {WS}/q90/cluster-events.sh P=$(kubectl -n kube-system get pod -l k8s-app=kube-proxy --field-selector spec.nodeName={W2} -o jsonpath='{.items[0].metadata.name}') kubectl -n kube-system delete pod $P for i in $(seq 1 40); do [ "$(kubectl -n kube-system get pod -l k8s-app=kube-proxy --field-selector spec.nodeName={W2} -o jsonpath='{.items[0].status.phase}' 2>/dev/null)" = Running ] && break; sleep 3 done sleep 5 kubectl -n kube-system get events --sort-by=.metadata.creationTimestamp | grep kube-proxy | tail -8 > {WS}/q90/pod-deleted.log ssh -n {W2} 'sudo crictl stop $(sudo crictl ps --name kube-proxy -q)' sleep 25 kubectl -n kube-system get events --sort-by=.metadata.creationTimestamp | grep kube-proxy | tail -4 > {WS}/q90/container-killed.log echo container > {WS}/q90/restarts.txt """, 92: r""" ssh -n {W1} "sudo sed -i 's|^ExecStart=/usr/local/bin/kubelet |ExecStart=/usr/bin/kubelet |' /etc/systemd/system/kubelet.service.d/10-kubeadm.conf; sudo systemctl daemon-reload; sudo systemctl restart kubelet" echo /etc/systemd/system/kubelet.service.d/10-kubeadm.conf > {WS}/q92/fixed-file.txt for i in $(seq 1 40); do [ "$(kubectl get node {W1} -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')" = True ] && break; sleep 3; done """, 94: r""" kubectl run ops-beacon -n default --image=nginx:1.27 --labels=app=ops-beacon --dry-run=client -o yaml \ | yq '.spec.containers[0].resources = {"requests": {"cpu": "10m", "memory": "20Mi"}}' | sudo tee /etc/kubernetes/manifests/ops-beacon.yaml >/dev/null for i in $(seq 1 60); do [ "$(kubectl -n default get pod ops-beacon-{CP} -o jsonpath='{.status.phase}' 2>/dev/null)" = Running ] && break; sleep 3; done kubectl -n default expose pod ops-beacon-{CP} --name=ops-beacon-svc --type=NodePort --port=80 --target-port=80 kubectl -n default get svc ops-beacon-svc -o jsonpath='{.spec.ports[0].nodePort}' > {WS}/q94/nodeport.txt sleep 5 """, 95: r""" CI=$(ssh -n {W2} 'sudo openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -issuer' | sed 's/^issuer= *//') SI=$(ssh -n {W2} 'sudo openssl x509 -in /var/lib/kubelet/pki/kubelet.crt -noout -issuer' | sed 's/^issuer= *//') printf 'client-issuer: %s\nclient-eku: TLS Web Client Authentication\nserver-issuer: %s\nserver-eku: TLS Web Server Authentication\n' "$CI" "$SI" > {WS}/q95/kubelet-certs.txt """, 96: r""" kubectl -n default run cp-watch --image=httpd:2-alpine --dry-run=client -o json | jq '.spec.containers[0].name = "watch" | .spec.nodeSelector = {"node-role.kubernetes.io/control-plane": ""} | .spec.tolerations = [{"key": "node-role.kubernetes.io/control-plane", "operator": "Exists", "effect": "NoSchedule"}]' | kubectl apply -f - kubectl -n default wait --for=condition=Ready pod/cp-watch --timeout=180s """, 97: CPWAIT + r""" sudo mv /etc/kubernetes/manifests/kube-scheduler.yaml /etc/kubernetes/ for i in $(seq 1 40); do [ -z "$(kubectl -n kube-system get pod -l component=kube-scheduler -o name 2>/dev/null)" ] && break; sleep 3; done kubectl -n default run manual-1 --image=busybox:1.36 -- sleep 86400 sleep 5 kubectl -n default get pod manual-1 -o json | jq 'del(.metadata.resourceVersion, .metadata.uid, .metadata.creationTimestamp, .status) | .spec.nodeName = "{CP}"' | kubectl replace --force -f - kubectl -n default wait --for=condition=Ready pod/manual-1 --timeout=180s sudo mv /etc/kubernetes/kube-scheduler.yaml /etc/kubernetes/manifests/ cpwait kube-scheduler kubectl -n default run manual-2 --image=busybox:1.36 -- sleep 86400 kubectl -n default wait --for=condition=Ready pod/manual-2 --timeout=180s """, 105: r""" kubectl -n %{ns} set resources deploy %{dep} -c web --requests=cpu=100m cat </dev/null && break; sleep 3; done """, 106: r""" echo Unhealthy > {WS}/q106/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 """, 107: r""" ssh -n {W3} "printf 'overlay\nbr_netfilter\n' | sudo tee /etc/modules-load.d/%{name}.conf >/dev/null printf 'net.bridge.bridge-nf-call-iptables = 1\nnet.bridge.bridge-nf-call-ip6tables = 1\nnet.ipv4.ip_forward = 1\n' | sudo tee /etc/sysctl.d/%{name}.conf >/dev/null" IPF=$(ssh -n {W3} 'sudo sysctl -n net.ipv4.ip_forward') SW=off; [ "$(ssh -n {W3} 'sed 1d /proc/swaps | grep -c .')" -gt 0 ] && SW=on FS=$(ssh -n {W3} "sudo awk '/^failSwapOn:/ {print \$2}' /var/lib/kubelet/config.yaml"); [ -n "$FS" ] || FS=true CG=$(ssh -n {W3} "sudo awk '/^cgroupDriver:/ {print \$2}' /var/lib/kubelet/config.yaml") printf 'ip_forward=%s\nswap=%s\nfail-swap-on=%s\ncgroup-driver=%s\n' "$IPF" "$SW" "$FS" "$CG" > {WS}/q107/node.txt """, 108: r""" V=$(ssh -n {W2} 'sudo crictl version 2>/dev/null') RN=$(echo "$V" | awk '/^RuntimeName:/ {print $2}'); RV=$(echo "$V" | awk '/^RuntimeVersion:/ {print $2}') SOCK=$(ssh -n {W2} "sudo awk '/^containerRuntimeEndpoint:/ {print \$2}' /var/lib/kubelet/config.yaml" | tr -d '"') CID=$(kubectl -n %{ns} get pod %{pod} -o jsonpath='{.status.containerStatuses[0].containerID}' | sed 's#^[a-z-]*://##') PID=$(ssh -n {W2} "sudo crictl inspect -o go-template --template '{{.info.pid}}' $CID 2>/dev/null") LOG=$(ssh -n {W2} "sudo crictl inspect -o go-template --template '{{.status.logPath}}' $CID 2>/dev/null") printf 'runtime=%s %s\nsocket=%s\ncontainer-id=%s\npid=%s\nlog=%s\n' "$RN" "$RV" "$SOCK" "$CID" "$PID" "$LOG" > {WS}/q108/runtime.txt """, 110: r""" kubectl -n kube-system get cm kubeadm-config -o jsonpath='{.data.ClusterConfiguration}' | awk '/^controlPlaneEndpoint:/{print $2}' | tr -d '"' > {WS}/q110/endpoint.txt KEY=$(sudo kubeadm init phase upload-certs --upload-certs 2>/dev/null | tail -1) sudo kubeadm token create --print-join-command --certificate-key $KEY 2>/dev/null | grep 'kubeadm join' > {WS}/q110/join-control-plane.sh """, 111: r""" E='sudo etcdctl --endpoints=https://127.0.0.1:2379 --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key' N=$(kubectl get nodes -l node-role.kubernetes.io/control-plane --no-headers | wc -l) M=$($E member list | wc -l) L=$($E member list | awk -F', ' 'NR==1 {print $3}') S=$(kubectl -n kube-system get lease kube-scheduler -o jsonpath='{.spec.holderIdentity}' | cut -d_ -f1) C=$(kubectl -n kube-system get lease kube-controller-manager -o jsonpath='{.spec.holderIdentity}' | cut -d_ -f1) printf 'control-plane-nodes=%s\netcd-topology=stacked\netcd-members=%s\netcd-leader=%s\nscheduler-leader=%s\ncontroller-manager-leader=%s\n' "$N" "$M" "$L" "$S" "$C" > {WS}/q111/ha.txt """, 112: r""" CONF=$(ssh -n {W1} "sudo sh -c 'ls /etc/cni/net.d/*.conflist | sort | head -1'") PL=$(ssh -n {W1} "sudo cat $CONF" | jq -r '[.plugins[].type] | join(",")') IPAM=$(ssh -n {W1} "sudo cat $CONF" | jq -r '[.plugins[] | select(.ipam) | .ipam.type][0]') SOCK=$(ssh -n {W1} "sudo awk '/^containerRuntimeEndpoint:/ {print \$2}' /var/lib/kubelet/config.yaml" | tr -d '"') N=$(kubectl get csidrivers --no-headers 2>/dev/null | wc -l) PROV=$(kubectl get sc -o json | jq -r '.items[] | select(.metadata.annotations["storageclass.kubernetes.io/is-default-class"] == "true") | .provisioner') printf 'cni-config=%s\ncni-plugins=%s\ncni-ipam=%s\ncri-socket=%s\ncsi-drivers=%s\ndefault-provisioner=%s\n' "$CONF" "$PL" "$IPAM" "$SOCK" "$N" "$PROV" > {WS}/q112/interfaces.txt """, 19: r""" for i in $(seq 1 40); do kubectl -n troubleshoot logs crasher --previous 2>/dev/null | grep -q DB_HOST && break; sleep 3; done kubectl -n troubleshoot logs crasher --previous > {WS}/q19/crasher.log kubectl -n troubleshoot get pod crasher -o json | jq 'del(.metadata.resourceVersion, .metadata.uid, .metadata.creationTimestamp, .status) | .spec.containers[0].env = [{"name": "DB_HOST", "value": "db.internal"}]' > /tmp/crasher.json kubectl -n troubleshoot delete pod crasher --now kubectl apply -f /tmp/crasher.json kubectl -n troubleshoot wait --for=condition=Ready pod/crasher --timeout=180s """, 41: r""" A=$(kubectl version -o json | jq -r .serverVersion.gitVersion) E=$(kubectl -n kube-system get pod -l component=etcd -o jsonpath='{.items[0].spec.containers[0].image}' | sed 's/.*://') KV=$(kubectl get node %{node} -o jsonpath='{.status.nodeInfo.kubeletVersion}') R=$(kubectl get node %{node} -o jsonpath='{.status.nodeInfo.containerRuntimeVersion}') printf 'apiserver=%s\netcd=%s\nkubelet=%s\nruntime=%s\n' "$A" "$E" "$KV" "$R" > {WS}/q41/versions.txt """, 59: r""" kubectl -n %{ns} apply -f - < /tmp/legacy.json kubectl -n %{ns} delete pod legacy --now kubectl apply -f /tmp/legacy.json kubectl -n %{ns} delete pod -l job-name=migrate kubectl -n %{ns} delete pvc old-data kubectl -n %{ns} wait --for=condition=Ready pod/legacy --timeout=180s """, 77: r""" kubectl -n kube-system get ds -o name | sed 's|.*/||' | grep -i kindnet > {WS}/q77/cni.txt kubectl -n zero-trust run srv --image=nginx:1.27 --labels=app=srv kubectl -n zero-trust expose pod srv --port 80 kubectl -n zero-trust run cli --image=busybox:1.36 -- sleep 3600 kubectl -n zero-trust wait --for=condition=Ready pod/srv pod/cli --timeout=180s kubectl -n zero-trust exec cli -- wget -qO- -T3 http://srv >/dev/null && echo reachable cat </dev/null 2>&1; then echo no; else echo yes; fi > {WS}/q77/enforced.txt kubectl -n zero-trust delete pod srv cli --now kubectl -n zero-trust delete svc srv """, 91: r""" kubectl api-resources --namespaced=true --no-headers | awk '{print $1}' > {WS}/q91/namespaced.txt for ns in census-north census-south census-east census-west; do echo "$ns $(kubectl -n $ns get role --no-headers | wc -l)"; done | sort -k2 -n | tail -1 > {WS}/q91/most-roles.txt """, 102: r""" kubectl -n oslo-files get pvc shared-files -o json | jq 'del(.metadata.resourceVersion, .metadata.uid, .metadata.creationTimestamp, .metadata.annotations, .metadata.finalizers, .status) | .spec.accessModes = ["ReadWriteOnce"]' > /tmp/pvc.json kubectl -n oslo-files delete pvc shared-files kubectl apply -f /tmp/pvc.json kubectl -n oslo-files rollout restart deploy uploader kubectl -n oslo-files rollout status deploy uploader --timeout=240s echo 'PVC requested ReadWriteMany, which the local-path provisioner cannot provide (RWO/RWOP only)' > {WS}/q102/reason.txt """, 93: r""" kubectl -n oslo-secure create secret generic bundle --from-file={WS}/q93/tls-bundle.pem kubectl -n oslo-secure create secret generic db-login --from-literal=user=svc-ledger --from-literal=pass='%{pw}' kubectl -n oslo-secure run vault-reader --image=busybox:1.36 --dry-run=client -o json -- sleep 86400 | jq ' .spec.volumes = [{"name": "bundle", "secret": {"secretName": "bundle"}}] | .spec.containers[0].volumeMounts = [{"name": "bundle", "mountPath": "/etc/bundle", "readOnly": true}] | .spec.containers[0].env = [{"name": "APP_USER", "valueFrom": {"secretKeyRef": {"name": "db-login", "key": "user"}}}, {"name": "APP_PASS", "valueFrom": {"secretKeyRef": {"name": "db-login", "key": "pass"}}}]' | kubectl apply -f - kubectl -n oslo-secure wait --for=condition=Ready pod/vault-reader --timeout=180s """, # ---- 132-145: bank_course.py (the others are solved by their solution as written, below) 132: r""" apt_get() { sudo DEBIAN_FRONTEND=noninteractive apt-get -qq -y -o Dpkg::Options::=--force-confold "$@" /dev/null; } sudo sed -i 's|/v1.36/|/v1.37/|' /etc/apt/sources.list.d/kubernetes.list apt_get update sudo apt-mark unhold kubeadm >/dev/null && apt_get install kubeadm=1.37.1-1.1 && sudo apt-mark hold kubeadm >/dev/null sudo kubeadm upgrade apply v1.37.1 -y /dev/null && apt_get install kubelet=1.37.1-1.1 kubectl=1.37.1-1.1 && sudo apt-mark hold kubelet kubectl >/dev/null sudo systemctl daemon-reload && sudo systemctl restart kubelet for i in $(seq 1 30); do kubectl uncordon {CP} >/dev/null 2>&1 && break; sleep 3; done kubectl drain {W1} --ignore-daemonsets --delete-emptydir-data ssh {W1} sudo bash <<'EOF' export DEBIAN_FRONTEND=noninteractive sed -i 's|/v1.36/|/v1.37/|' /etc/apt/sources.list.d/kubernetes.list apt-get update -qq /dev/null apt-get install -y -qq kubeadm=1.37.1-1.1 /dev/null kubeadm upgrade node /dev/null apt-mark hold kubeadm kubelet kubectl >/dev/null systemctl daemon-reload && systemctl restart kubelet EOF kubectl uncordon {W1} """, 139: r""" ssh {W1} sudo bash <<'EOF' yq -i '.evictionHard."memory.available" = "%{mem}Mi" | .evictionHard."nodefs.available" = "%{fs}%" | .evictionSoft."memory.available" = "%{soft}Mi" | .evictionSoftGracePeriod."memory.available" = "1m30s"' /var/lib/kubelet/config.yaml systemctl restart kubelet EOF for i in $(seq 1 30); do [ "$(kubectl get node {W1} -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')" = True ] && break; sleep 3; done """, } # ---- 1-113: the banks carried over from cka-sim. # For these tasks the reference solution is a script as it stands, so the audit runs exactly the # text the candidate is shown - and a solution that stops working is found the next time it runs. AS_WRITTEN = [1, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 40, 42, 43, 44, 47, 48, 49, 50, 52, 53, 54, 55, 56, 58, 62, 63, 64, 68, 69, 72, 73, 74, 75, 78, 80, 82, 83, 85, 89, 98, 99, 100, 101, 103, 104, 109, 113, # bank_course.py: written to run as they stand 133, 134, 135, 136, 137, 138, 140, 141, 142, 143, 144, 145, 146, 147] def _as_written(): from cka.questions import QUESTIONS text = {q["id"]: q["solution"] for q in QUESTIONS} # $do is the shorthand the solutions use for kubectl's dry run (export it yourself in the exam) return {i: 'export do="--dry-run=client -o yaml"\n' + text[i] for i in AS_WRITTEN if i not in SOLVE} SOLVE.update(_as_written())