diff --git a/main.go b/main.go index 6935b50..4b5194a 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" agentv1 "github.com/appuio/appuio-cloud-agent/api/v1" "github.com/appuio/appuio-cloud-agent/controllers" @@ -179,8 +180,10 @@ func main() { mgr.GetWebhookServer().Register("/validate-namespace-quota", &webhook.Admission{ Handler: &webhooks.NamespaceQuotaValidator{ - Skipper: psk, Client: mgr.GetClient(), + Decoder: admission.NewDecoder(mgr.GetScheme()), + + Skipper: psk, OrganizationLabel: conf.OrganizationLabel, UserDefaultOrganizationAnnotation: conf.UserDefaultOrganizationAnnotation, @@ -220,8 +223,11 @@ func main() { func registerNodeSelectorValidationWebhooks(mgr ctrl.Manager, conf Config) { mgr.GetWebhookServer().Register("/mutate-pod-node-selector", &webhook.Admission{ Handler: &webhooks.PodNodeSelectorMutator{ - Skipper: skipper.StaticSkipper{ShouldSkip: false}, - Client: mgr.GetClient(), + Client: mgr.GetClient(), + Decoder: admission.NewDecoder(mgr.GetScheme()), + + Skipper: skipper.StaticSkipper{ShouldSkip: false}, + DefaultNodeSelector: conf.DefaultNodeSelector, DefaultNamespaceNodeSelectorAnnotation: conf.DefaultNamespaceNodeSelectorAnnotation, }, @@ -248,7 +254,9 @@ func registerRatioController(mgr ctrl.Manager, conf Config, orgLabel string) { DefaultNodeSelector: conf.DefaultNodeSelector, DefaultNamespaceNodeSelectorAnnotation: conf.DefaultNamespaceNodeSelectorAnnotation, - Client: mgr.GetClient(), + Decoder: admission.NewDecoder(mgr.GetScheme()), + Client: mgr.GetClient(), + RatioLimits: conf.MemoryPerCoreLimits, Ratio: &ratio.Fetcher{ Client: mgr.GetClient(), diff --git a/webhooks/namespace_quota_validator.go b/webhooks/namespace_quota_validator.go index 470e467..63d89c8 100644 --- a/webhooks/namespace_quota_validator.go +++ b/webhooks/namespace_quota_validator.go @@ -27,7 +27,7 @@ import ( // NamespaceQuotaValidator checks namespaces for allowed node selectors. type NamespaceQuotaValidator struct { - decoder *admission.Decoder + Decoder *admission.Decoder // Client is used to fetch namespace counts Client client.Reader @@ -64,7 +64,7 @@ func (v *NamespaceQuotaValidator) Handle(ctx context.Context, req admission.Requ // try to get the organization name from the namespace/projectrequest var rawObject unstructured.Unstructured - if err := v.decoder.Decode(req, &rawObject); err != nil { + if err := v.Decoder.Decode(req, &rawObject); err != nil { l.Error(err, "failed to decode request") return admission.Errored(http.StatusBadRequest, err) } @@ -131,9 +131,3 @@ func (v *NamespaceQuotaValidator) Handle(ctx context.Context, req admission.Requ return admission.Allowed("allowed") } - -// InjectDecoder injects a Admission request decoder -func (v *NamespaceQuotaValidator) InjectDecoder(d *admission.Decoder) error { - v.decoder = d - return nil -} diff --git a/webhooks/namespace_quota_validator_test.go b/webhooks/namespace_quota_validator_test.go index 102b573..baa9219 100644 --- a/webhooks/namespace_quota_validator_test.go +++ b/webhooks/namespace_quota_validator_test.go @@ -164,6 +164,7 @@ func TestNamespaceQuotaValidator_Handle(t *testing.T) { t.Run(name, func(t *testing.T) { c, scheme, dec := prepareClient(t, test.initObjects...) subject := &NamespaceQuotaValidator{ + Decoder: dec, Client: c, Skipper: skipper.StaticSkipper{ShouldSkip: false}, @@ -173,7 +174,6 @@ func TestNamespaceQuotaValidator_Handle(t *testing.T) { SelectedProfile: "test", QuotaOverrideNamespace: "test", } - subject.InjectDecoder(dec) require.NoError(t, c.Create(ctx, &cloudagentv1.ZoneUsageProfile{ ObjectMeta: metav1.ObjectMeta{ diff --git a/webhooks/pod_node_selector_mutator.go b/webhooks/pod_node_selector_mutator.go index 50b6788..5e63fda 100644 --- a/webhooks/pod_node_selector_mutator.go +++ b/webhooks/pod_node_selector_mutator.go @@ -20,7 +20,7 @@ import ( // PodNodeSelectorMutator checks namespaces for allowed node selectors. type PodNodeSelectorMutator struct { - decoder *admission.Decoder + Decoder *admission.Decoder // Client is used to fetch namespace metadata Client client.Reader @@ -63,7 +63,7 @@ func (v *PodNodeSelectorMutator) Handle(ctx context.Context, req admission.Reque } var rawPod unstructured.Unstructured - if err := v.decoder.Decode(req, &rawPod); err != nil { + if err := v.Decoder.Decode(req, &rawPod); err != nil { l.Error(err, "failed to decode request") return admission.Errored(400, err) } @@ -103,12 +103,6 @@ func (v *PodNodeSelectorMutator) Handle(ctx context.Context, req admission.Reque return admission.Patched("added default node selector", patches...) } -// InjectDecoder injects a Admission request decoder -func (v *PodNodeSelectorMutator) InjectDecoder(d *admission.Decoder) error { - v.decoder = d - return nil -} - func (v *PodNodeSelectorMutator) defaultLabels(ns corev1.Namespace) (labels.Set, error) { rawDefaults := ns.Annotations[v.DefaultNamespaceNodeSelectorAnnotation] if v.DefaultNamespaceNodeSelectorAnnotation == "" || rawDefaults == "" { diff --git a/webhooks/pod_node_selector_mutator_test.go b/webhooks/pod_node_selector_mutator_test.go index e3ede92..6d28002 100644 --- a/webhooks/pod_node_selector_mutator_test.go +++ b/webhooks/pod_node_selector_mutator_test.go @@ -86,12 +86,12 @@ func Test_PodNodeSelectorMutator_Handle(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { subject := PodNodeSelectorMutator{ + Decoder: decoder, Client: c, Skipper: skipper.StaticSkipper{}, DefaultNodeSelector: tc.defaultNodeSelector, DefaultNamespaceNodeSelectorAnnotation: nodeSelAnnotation, } - subject.InjectDecoder(decoder) pod := newPod(tc.namespace, "test", tc.nodeSelector) resp := subject.Handle(context.Background(), admissionRequestForObject(t, pod, scheme)) diff --git a/webhooks/ratio_validator.go b/webhooks/ratio_validator.go index 810d16c..7f38795 100644 --- a/webhooks/ratio_validator.go +++ b/webhooks/ratio_validator.go @@ -27,7 +27,7 @@ import ( // RatioValidator checks for every action in a namespace whether the Memory to CPU ratio limit is exceeded and will return a warning if it is. type RatioValidator struct { - decoder *admission.Decoder + Decoder *admission.Decoder Client client.Client Ratio ratioFetcher @@ -135,19 +135,19 @@ func (v *RatioValidator) recordObject(ctx context.Context, r *ratio.Ratio, req a switch req.Kind.Kind { case "Pod": pod := corev1.Pod{} - if err := v.decoder.Decode(req, &pod); err != nil { + if err := v.Decoder.Decode(req, &pod); err != nil { return r, err } r = r.RecordPod(pod) case "Deployment": deploy := appsv1.Deployment{} - if err := v.decoder.Decode(req, &deploy); err != nil { + if err := v.Decoder.Decode(req, &deploy); err != nil { return r, err } r = r.RecordDeployment(deploy) case "StatefulSet": sts := appsv1.StatefulSet{} - if err := v.decoder.Decode(req, &sts); err != nil { + if err := v.Decoder.Decode(req, &sts); err != nil { return r, err } r = r.RecordStatefulSet(sts) @@ -159,19 +159,19 @@ func (v *RatioValidator) getNodeSelector(req admission.Request) (map[string]stri switch req.Kind.Kind { case "Pod": pod := corev1.Pod{} - if err := v.decoder.Decode(req, &pod); err != nil { + if err := v.Decoder.Decode(req, &pod); err != nil { return nil, err } return pod.Spec.NodeSelector, nil case "Deployment": deploy := appsv1.Deployment{} - if err := v.decoder.Decode(req, &deploy); err != nil { + if err := v.Decoder.Decode(req, &deploy); err != nil { return nil, err } return deploy.Spec.Template.Spec.NodeSelector, nil case "StatefulSet": sts := appsv1.StatefulSet{} - if err := v.decoder.Decode(req, &sts); err != nil { + if err := v.Decoder.Decode(req, &sts); err != nil { return nil, err } return sts.Spec.Template.Spec.NodeSelector, nil @@ -188,12 +188,6 @@ func (v *RatioValidator) getDefaultNodeSelectorFromNamespace(ctx context.Context return labels.ConvertSelectorToLabelsMap(ns.Annotations[v.DefaultNamespaceNodeSelectorAnnotation]) } -// InjectDecoder injects a Admission request decoder -func (v *RatioValidator) InjectDecoder(d *admission.Decoder) error { - v.decoder = d - return nil -} - func errored(code int32, err error) admission.Response { return admission.Response{ AdmissionResponse: admissionv1.AdmissionResponse{ diff --git a/webhooks/ratio_validator_test.go b/webhooks/ratio_validator_test.go index b823b67..723f1f1 100644 --- a/webhooks/ratio_validator_test.go +++ b/webhooks/ratio_validator_test.go @@ -413,13 +413,13 @@ func prepareTest(t *testing.T, initObjs ...client.Object) *RatioValidator { uv := &RatioValidator{ DefaultNamespaceNodeSelectorAnnotation: defaultNodeSelectorAnnotation, - Client: failingClient{client}, + Decoder: decoder, + Client: failingClient{client}, Ratio: ratio.Fetcher{ Client: failingClient{client}, }, } - uv.InjectDecoder(decoder) return uv }