package main import ( "context" "errors" admission "k8s.io/api/admission/v1" corev1 "k8s.io/api/core/v1" admv1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/klog/v2" ) const ( requiredLabelKey = "app" requiredLabelVal = "my-app" ) func validatePodLabels(attributes *admission.Attributes) error { pod := &corev1.Pod{} request := attributes.Request.Object.Raw if err := json.NewDecoder(bytes.NewReader(request)).Decode(pod); err != nil { return err } labels := pod.GetLabels() if labels == nil || labels[requiredLabelKey] != requiredLabelVal { return errors.New("pod does not have required label") } return nil } func validatePodHandler(w http.ResponseWriter, r *http.Request) { var admissionResponse *admv1.AdmissionResponse admissionReview := &admv1.AdmissionReview{} json.NewDecoder(r.Body).Decode(admissionReview) attributes := &admission.Attributes{ AdmissionReview: admissionReview, } if err := validatePodLabels(attributes); err != nil { admissionResponse = &admv1.AdmissionResponse{ Allowed: false, Result: &metav1.Status{ Message: err.Error(), }, } } else { admissionResponse = &admv1.AdmissionResponse{ Allowed: true, } } admissionReview.Response = admissionResponse responseData, err := json.Marshal(admissionReview) if err != nil { klog.Errorf("Error marshalling admission review for pod validation: %v", err) } w.Header().Set("Content-Type", "application/json") if _, err := w.Write(responseData); err != nil { klog.Errorf("Error writing admission review for pod validation: %v", err) } } func main() { http.HandleFunc("/validate-pod", validatePodHandler) http.ListenAndServe(":8080", nil) }
package main import ( "context" "errors" admission "k8s.io/api/admission/v1" admv1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/klog/v2" ) var allowedNamespaces = map[string]bool{ "my-namespace-1": true, "my-namespace-2": true, } func validateNamespace(attributes *admission.Attributes) error { objectMeta := attributes.Request.Object.GetObjectMeta() namespace := objectMeta.GetNamespace() if !allowedNamespaces[namespace] { return errors.New("namespace not allowed") } return nil } func validateNamespaceHandler(w http.ResponseWriter, r *http.Request) { var admissionResponse *admv1.AdmissionResponse admissionReview := &admv1.AdmissionReview{} json.NewDecoder(r.Body).Decode(admissionReview) attributes := &admission.Attributes{ AdmissionReview: admissionReview, } if err := validateNamespace(attributes); err != nil { admissionResponse = &admv1.AdmissionResponse{ Allowed: false, Result: &metav1.Status{ Message: err.Error(), }, } } else { admissionResponse = &admv1.AdmissionResponse{ Allowed: true, } } admissionReview.Response = admissionResponse responseData, err := json.Marshal(admissionReview) if err != nil { klog.Errorf("Error marshalling admission review for namespace validation: %v", err) } w.Header().Set("Content-Type", "application/json") if _, err := w.Write(responseData); err != nil { klog.Errorf("Error writing admission review for namespace validation: %v", err) } } func main() { http.HandleFunc("/validate-namespace", validateNamespaceHandler) http.ListenAndServe(":8080", nil) }This example validates that a Kubernetes resource can only be created or updated in certain namespaces. It checks the namespace of the object's metadata against a pre-defined whitelist of allowed namespaces. In summary, the go k8s.io/kubernetes/pkg/admission package library provides functionality for validating and mutating Kubernetes resources before they are persisted to etcd. It can be used to enforce custom validation rules, perform data transformations, and more.