diff --git a/internal/operations/podsMutation.go b/internal/operations/podsMutation.go index 0eb9027..35d8f14 100644 --- a/internal/operations/podsMutation.go +++ b/internal/operations/podsMutation.go @@ -1,5 +1,64 @@ package operations +import ( + "fmt" + "log" + + admission "k8s.io/api/admission/v1" + v1 "k8s.io/api/core/v1" +) + func PodsMutation() Hook { - return Hook{} + return Hook{ + Create: podMutationCreate(), + // default allow + Delete: func(r *admission.AdmissionRequest) (*Result, error) { + return &Result{Allowed: true}, nil + }, + Update: func(r *admission.AdmissionRequest) (*Result, error) { + return &Result{Allowed: true}, nil + }, + Connect: func(r *admission.AdmissionRequest) (*Result, error) { + return &Result{Allowed: true}, nil + }, + } +} + +func podMutationCreate() AdmitFunc { + return func(r *admission.AdmissionRequest) (*Result, error) { + var operations []PatchOperation + pod, err := parsePod(r.Object.Raw) + if err != nil { + return &Result{Msg: err.Error()}, nil + } + + // if pod is administratively exempt + if func(pod *v1.Pod) bool { + for label, value := range pod.Annotations { + log.Printf("[TRACE] Checking Metadata: %s=%s", label, value) + if label == "AdminNoMutate" && value == "true" { + return false + } + } + return true + }(pod) { + // mutate pod (annotation) + metadata := map[string]string{ + "mutation-status": "pod mutated by mutation-controller", + } + // add original image to annotations + for _, p := range pod.Spec.Containers { + metadata[fmt.Sprintf("mutation-original-image-%s", p.Name)] = p.Image + } + // add annotation stating that the pos had been mutated + operations = append(operations, AddPatchOperation("/metadata/annotations", metadata)) + + // add image mutation + } + + return &Result{ + Allowed: true, + PatchOps: operations, + }, nil + } } diff --git a/mock-payloads/pods/test-pod01.json b/mock-payloads/pods/test-pod01.json index 0c1263f..d688809 100644 --- a/mock-payloads/pods/test-pod01.json +++ b/mock-payloads/pods/test-pod01.json @@ -41,54 +41,8 @@ "run": "toolbox" }, "annotations": { - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"labels\":{\"run\":\"toolbox\"},\"name\":\"test-pod01\",\"namespace\":\"test1\"},\"spec\":{\"containers\":[{\"image\":\"jmsearcy/toolbox:latest\",\"name\":\"toolbox\",\"ports\":[{\"containerPort\":8080}]}]}}\n" - }, - "managedFields": [{ - "manager": "kubectl", - "operation": "Update", - "apiVersion": "v1", - "time": "2020-10-11T05:15:41Z", - "fieldsType": "FieldsV1", - "fieldsV1": { - "f:metadata": { - "f:annotations": { - ".": {}, - "f:kubectl.kubernetes.io/last-applied-configuration": {} - }, - "f:labels": { - ".": {}, - "f:run": {} - } - }, - "f:spec": { - "f:containers": { - "k:{\"name\":\"toolbox\"}": { - ".": {}, - "f:image": {}, - "f:imagePullPolicy": {}, - "f:name": {}, - "f:ports": { - ".": {}, - "k:{\"containerPort\":8080,\"protocol\":\"TCP\"}": { - ".": {}, - "f:containerPort": {}, - "f:protocol": {} - } - }, - "f:resources": {}, - "f:terminationMessagePath": {}, - "f:terminationMessagePolicy": {} - } - }, - "f:dnsPolicy": {}, - "f:enableServiceLinks": {}, - "f:restartPolicy": {}, - "f:schedulerName": {}, - "f:securityContext": {}, - "f:terminationGracePeriodSeconds": {} - } - } - }] + "AdminNoMutate": "false" + } }, "spec": { "volumes": [{