diff --git a/pkg/admissioncontroller/webhook/admission/internaldomainsecret/handler.go b/pkg/admissioncontroller/webhook/admission/internaldomainsecret/handler.go index 7be9d9a4038..f9509ff2e92 100644 --- a/pkg/admissioncontroller/webhook/admission/internaldomainsecret/handler.go +++ b/pkg/admissioncontroller/webhook/admission/internaldomainsecret/handler.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" gardencore "github.com/gardener/gardener/pkg/apis/core" gardencorev1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" @@ -40,53 +41,53 @@ type Handler struct { } // ValidateCreate performs the check. -func (h *Handler) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (h *Handler) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() secret, ok := obj.(*corev1.Secret) if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected *corev1.Secret but got %T", obj)) + return nil, apierrors.NewBadRequest(fmt.Sprintf("expected *corev1.Secret but got %T", obj)) } seedName := gardenerutils.ComputeSeedName(secret.Namespace) if secret.Namespace != v1beta1constants.GardenNamespace && seedName == "" { - return nil + return nil, nil } exists, err := h.internalDomainSecretExists(ctx, secret.Namespace) if err != nil { - return apierrors.NewInternalError(err) + return nil, apierrors.NewInternalError(err) } if exists { - return apierrors.NewConflict(schema.GroupResource{Group: corev1.GroupName, Resource: "Secret"}, secret.Name, fmt.Errorf("cannot create internal domain secret because there can be only one secret with the 'internal-domain' secret role per namespace")) + return nil, apierrors.NewConflict(schema.GroupResource{Group: corev1.GroupName, Resource: "Secret"}, secret.Name, fmt.Errorf("cannot create internal domain secret because there can be only one secret with the 'internal-domain' secret role per namespace")) } if _, _, _, _, _, err := gardenerutils.GetDomainInfoFromAnnotations(secret.Annotations); err != nil { - return apierrors.NewBadRequest(err.Error()) + return nil, apierrors.NewBadRequest(err.Error()) } - return nil + return nil, nil } // ValidateUpdate performs the check. -func (h *Handler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (h *Handler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() secret, ok := newObj.(*corev1.Secret) if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected *corev1.Secret but got %T", newObj)) + return nil, apierrors.NewBadRequest(fmt.Sprintf("expected *corev1.Secret but got %T", newObj)) } oldSecret, ok := oldObj.(*corev1.Secret) if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected *corev1.Secret but got %T", oldObj)) + return nil, apierrors.NewBadRequest(fmt.Sprintf("expected *corev1.Secret but got %T", oldObj)) } seedName := gardenerutils.ComputeSeedName(secret.Namespace) if secret.Namespace != v1beta1constants.GardenNamespace && seedName == "" { - return nil + return nil, nil } // If secret was newly labeled with gardener.cloud/role=internal-domain then check whether another internal domain @@ -95,59 +96,59 @@ func (h *Handler) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Obj secret.Labels[v1beta1constants.GardenRole] == v1beta1constants.GardenRoleInternalDomain { exists, err := h.internalDomainSecretExists(ctx, secret.Namespace) if err != nil { - return apierrors.NewInternalError(err) + return nil, apierrors.NewInternalError(err) } if exists { - return apierrors.NewConflict(schema.GroupResource{Group: corev1.GroupName, Resource: "Secret"}, secret.Name, fmt.Errorf("cannot update secret because there can be only one secret with the 'internal-domain' secret role per namespace")) + return nil, apierrors.NewConflict(schema.GroupResource{Group: corev1.GroupName, Resource: "Secret"}, secret.Name, fmt.Errorf("cannot update secret because there can be only one secret with the 'internal-domain' secret role per namespace")) } } _, oldDomain, _, _, _, err := gardenerutils.GetDomainInfoFromAnnotations(oldSecret.Annotations) if err != nil { - return apierrors.NewInternalError(err) + return nil, apierrors.NewInternalError(err) } _, newDomain, _, _, _, err := gardenerutils.GetDomainInfoFromAnnotations(secret.Annotations) if err != nil { - return apierrors.NewInternalError(err) + return nil, apierrors.NewInternalError(err) } if oldDomain != newDomain { atLeastOneShoot, err := h.atLeastOneShootExists(ctx, seedName) if err != nil { - return apierrors.NewInternalError(err) + return nil, apierrors.NewInternalError(err) } if atLeastOneShoot { - return apierrors.NewForbidden(schema.GroupResource{Group: corev1.GroupName, Resource: "Secret"}, secret.Name, fmt.Errorf("cannot change domain because there are still shoots left in the system")) + return nil, apierrors.NewForbidden(schema.GroupResource{Group: corev1.GroupName, Resource: "Secret"}, secret.Name, fmt.Errorf("cannot change domain because there are still shoots left in the system")) } } - return nil + return nil, nil } // ValidateDelete performs the check. -func (h *Handler) ValidateDelete(ctx context.Context, obj runtime.Object) error { +func (h *Handler) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() secret, ok := obj.(*corev1.Secret) if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected *corev1.Secret but got %T", obj)) + return nil, apierrors.NewBadRequest(fmt.Sprintf("expected *corev1.Secret but got %T", obj)) } seedName := gardenerutils.ComputeSeedName(secret.Namespace) if secret.Namespace != v1beta1constants.GardenNamespace && seedName == "" { - return nil + return nil, nil } atLeastOneShoot, err := h.atLeastOneShootExists(ctx, seedName) if err != nil { - return apierrors.NewInternalError(err) + return nil, apierrors.NewInternalError(err) } if atLeastOneShoot { - return apierrors.NewForbidden(schema.GroupResource{Group: corev1.GroupName, Resource: "Secret"}, secret.Name, fmt.Errorf("cannot delete internal domain secret because there are still shoots left in the system")) + return nil, apierrors.NewForbidden(schema.GroupResource{Group: corev1.GroupName, Resource: "Secret"}, secret.Name, fmt.Errorf("cannot delete internal domain secret because there are still shoots left in the system")) } - return nil + return nil, nil } func (h *Handler) atLeastOneShootExists(ctx context.Context, seedName string) (bool, error) { diff --git a/pkg/admissioncontroller/webhook/admission/internaldomainsecret/handler_test.go b/pkg/admissioncontroller/webhook/admission/internaldomainsecret/handler_test.go index 8795022221d..c71c32ee302 100644 --- a/pkg/admissioncontroller/webhook/admission/internaldomainsecret/handler_test.go +++ b/pkg/admissioncontroller/webhook/admission/internaldomainsecret/handler_test.go @@ -111,7 +111,9 @@ var _ = Describe("handler", func() { client.Limit(1), ).Return(fakeErr) - err := handler.ValidateCreate(ctx, secret) + warnings, err := handler.ValidateCreate(ctx, secret) + Expect(warnings).To(BeNil()) + statusError, ok := err.(*apierrors.StatusError) Expect(ok).To(BeTrue()) Expect(statusError.Status().Code).To(Equal(int32(http.StatusInternalServerError))) @@ -130,7 +132,9 @@ var _ = Describe("handler", func() { return nil }) - Expect(handler.ValidateCreate(ctx, secret)).To(MatchError(ContainSubstring("there can be only one secret with the 'internal-domain' secret role"))) + warnings, err := handler.ValidateCreate(ctx, secret) + Expect(warnings).To(BeNil()) + Expect(err).To(MatchError(ContainSubstring("there can be only one secret with the 'internal-domain' secret role"))) }) It("should fail because another internal domain secret exists in the same seed namespace", func() { @@ -193,7 +197,9 @@ var _ = Describe("handler", func() { client.Limit(1), ).Return(fakeErr) - err := handler.ValidateUpdate(ctx, oldSecret, secret) + warnings, err := handler.ValidateUpdate(ctx, oldSecret, secret) + Expect(warnings).To(BeNil()) + statusError, ok := err.(*apierrors.StatusError) Expect(ok).To(BeTrue()) Expect(statusError.Status().Code).To(Equal(int32(http.StatusInternalServerError))) @@ -238,7 +244,9 @@ var _ = Describe("handler", func() { oldSecret := secret.DeepCopy() secret.Annotations["dns.gardener.cloud/domain"] = "foobar" - err := handler.ValidateUpdate(ctx, oldSecret, secret) + warnings, err := handler.ValidateUpdate(ctx, oldSecret, secret) + Expect(warnings).To(BeNil()) + statusError, ok := err.(*apierrors.StatusError) Expect(ok).To(BeTrue()) Expect(statusError.Status().Code).To(Equal(int32(http.StatusInternalServerError))) @@ -304,7 +312,9 @@ var _ = Describe("handler", func() { client.Limit(1), ).Return(fakeErr) - err := handler.ValidateDelete(ctx, secret) + warnings, err := handler.ValidateDelete(ctx, secret) + Expect(warnings).To(BeNil()) + statusError, ok := err.(*apierrors.StatusError) Expect(ok).To(BeTrue()) Expect(statusError.Status().Code).To(Equal(int32(http.StatusInternalServerError))) diff --git a/pkg/admissioncontroller/webhook/admission/kubeconfigsecret/handler.go b/pkg/admissioncontroller/webhook/admission/kubeconfigsecret/handler.go index cc638ce422c..f9f6370524f 100644 --- a/pkg/admissioncontroller/webhook/admission/kubeconfigsecret/handler.go +++ b/pkg/admissioncontroller/webhook/admission/kubeconfigsecret/handler.go @@ -41,46 +41,46 @@ type Handler struct { } // ValidateCreate performs the check. -func (h *Handler) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (h *Handler) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { return h.handle(ctx, obj) } // ValidateUpdate performs the check. -func (h *Handler) ValidateUpdate(ctx context.Context, _, newObj runtime.Object) error { +func (h *Handler) ValidateUpdate(ctx context.Context, _, newObj runtime.Object) (admission.Warnings, error) { return h.handle(ctx, newObj) } // ValidateDelete returns nil (not implemented by this handler). -func (h *Handler) ValidateDelete(_ context.Context, _ runtime.Object) error { - return nil +func (h *Handler) ValidateDelete(_ context.Context, _ runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (h *Handler) handle(ctx context.Context, obj runtime.Object) error { +func (h *Handler) handle(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { secret, ok := obj.(*corev1.Secret) if !ok { - return apierrors.NewBadRequest(fmt.Sprintf("expected *corev1.Secret but got %T", obj)) + return nil, apierrors.NewBadRequest(fmt.Sprintf("expected *corev1.Secret but got %T", obj)) } req, err := admission.RequestFromContext(ctx) if err != nil { - return apierrors.NewInternalError(err) + return nil, apierrors.NewInternalError(err) } kubeconfig, ok := secret.Data[kubernetes.KubeConfig] if !ok { - return nil + return nil, nil } clientConfig, err := clientcmd.NewClientConfigFromBytes(kubeconfig) if err != nil { - return apierrors.NewBadRequest(err.Error()) + return nil, apierrors.NewBadRequest(err.Error()) } // Validate that the given kubeconfig doesn't have fields in its auth-info that are // not acceptable. rawConfig, err := clientConfig.RawConfig() if err != nil { - return apierrors.NewBadRequest(err.Error()) + return nil, apierrors.NewBadRequest(err.Error()) } if err := kubernetes.ValidateConfig(rawConfig); err != nil { @@ -98,8 +98,8 @@ func (h *Handler) handle(ctx context.Context, obj runtime.Object) error { metricReasonRejectedKubeconfig, ).Inc() - return apierrors.NewInvalid(schema.GroupKind{Group: corev1.GroupName, Kind: "Secret"}, secret.Name, field.ErrorList{field.Invalid(field.NewPath("data", "kubeconfig"), kubeconfig, fmt.Sprintf("secret contains invalid kubeconfig: %s", err))}) + return nil, apierrors.NewInvalid(schema.GroupKind{Group: corev1.GroupName, Kind: "Secret"}, secret.Name, field.ErrorList{field.Invalid(field.NewPath("data", "kubeconfig"), kubeconfig, fmt.Sprintf("secret contains invalid kubeconfig: %s", err))}) } - return nil + return nil, nil } diff --git a/pkg/admissioncontroller/webhook/admission/namespacedeletion/handler.go b/pkg/admissioncontroller/webhook/admission/namespacedeletion/handler.go index 1da6f62e32a..386c70ea7b3 100644 --- a/pkg/admissioncontroller/webhook/admission/namespacedeletion/handler.go +++ b/pkg/admissioncontroller/webhook/admission/namespacedeletion/handler.go @@ -40,23 +40,23 @@ type Handler struct { } // ValidateCreate returns nil (not implemented by this handler). -func (h *Handler) ValidateCreate(_ context.Context, _ runtime.Object) error { - return nil +func (h *Handler) ValidateCreate(_ context.Context, _ runtime.Object) (admission.Warnings, error) { + return nil, nil } // ValidateUpdate returns nil (not implemented by this handler). -func (h *Handler) ValidateUpdate(_ context.Context, _, _ runtime.Object) error { - return nil +func (h *Handler) ValidateUpdate(_ context.Context, _, _ runtime.Object) (admission.Warnings, error) { + return nil, nil } // ValidateDelete validates the namespace deletion. -func (h *Handler) ValidateDelete(ctx context.Context, _ runtime.Object) error { +func (h *Handler) ValidateDelete(ctx context.Context, _ runtime.Object) (admission.Warnings, error) { ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() req, err := admission.RequestFromContext(ctx) if err != nil { - return apierrors.NewInternalError(err) + return nil, apierrors.NewInternalError(err) } if err := h.admitNamespace(ctx, req.Name); err != nil { @@ -64,11 +64,11 @@ func (h *Handler) ValidateDelete(ctx context.Context, _ runtime.Object) error { return err } - return nil + return nil, nil } // admitNamespace does only allow the request if no Shoots exist in this specific namespace anymore. -func (h *Handler) admitNamespace(ctx context.Context, namespaceName string) error { +func (h *Handler) admitNamespace(ctx context.Context, namespaceName string) (admission.Warnings, error) { // Determine project for given namespace. // TODO: we should use a direct lookup here, as we might falsely allow the request, if our cache is // out of sync and doesn't know about the project. We should use a field selector for looking up the project @@ -76,34 +76,34 @@ func (h *Handler) admitNamespace(ctx context.Context, namespaceName string) erro project, namespace, err := gardenerutils.ProjectAndNamespaceFromReader(ctx, h.Client, namespaceName) if err != nil { if apierrors.IsNotFound(err) { - return nil + return nil, nil } - return apierrors.NewInternalError(err) + return nil, apierrors.NewInternalError(err) } if project == nil { - return nil + return nil, nil } switch { case namespace.DeletionTimestamp != nil: - return nil + return nil, nil case project.DeletionTimestamp != nil: // if project is marked for deletion we need to wait until all shoots in the namespace are gone namespaceInUse, err := kubernetesutils.ResourcesExist(ctx, h.APIReader, gardencorev1beta1.SchemeGroupVersion.WithKind("ShootList"), client.InNamespace(namespace.Name)) if err != nil { - return apierrors.NewInternalError(err) + return nil, apierrors.NewInternalError(err) } if !namespaceInUse { - return nil + return nil, nil } - return apierrors.NewForbidden(schema.GroupResource{Group: corev1.GroupName, Resource: "Namespace"}, namespace.Name, fmt.Errorf("deletion of namespace %q is not permitted (it still contains Shoots)", namespace.Name)) + return nil, apierrors.NewForbidden(schema.GroupResource{Group: corev1.GroupName, Resource: "Namespace"}, namespace.Name, fmt.Errorf("deletion of namespace %q is not permitted (it still contains Shoots)", namespace.Name)) } // Namespace is not yet marked for deletion and project is not marked as well. We do not admit and respond that // namespace deletion is only allowed via project deletion. - return apierrors.NewForbidden(schema.GroupResource{Group: corev1.GroupName, Resource: "Namespace"}, namespace.Name, fmt.Errorf("direct deletion of namespace %q is not permitted (you must delete the corresponding project %q)", namespace.Name, project.Name)) + return nil, apierrors.NewForbidden(schema.GroupResource{Group: corev1.GroupName, Resource: "Namespace"}, namespace.Name, fmt.Errorf("direct deletion of namespace %q is not permitted (you must delete the corresponding project %q)", namespace.Name, project.Name)) } diff --git a/pkg/operator/webhook/validation/handler.go b/pkg/operator/webhook/validation/handler.go index eb81448af2f..b64c0969286 100644 --- a/pkg/operator/webhook/validation/handler.go +++ b/pkg/operator/webhook/validation/handler.go @@ -22,6 +22,7 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" operatorv1alpha1 "github.com/gardener/gardener/pkg/apis/operator/v1alpha1" "github.com/gardener/gardener/pkg/apis/operator/v1alpha1/validation" @@ -35,60 +36,60 @@ type Handler struct { RuntimeClient client.Client } -func validate(obj runtime.Object) error { +func validate(obj runtime.Object) (admission.Warnings, error) { garden, ok := obj.(*operatorv1alpha1.Garden) if !ok { - return fmt.Errorf("expected *operatorv1alpha1.Garden but got %T", obj) + return nil, fmt.Errorf("expected *operatorv1alpha1.Garden but got %T", obj) } if errs := validation.ValidateGarden(garden); len(errs) > 0 { - return apierrors.NewInvalid(operatorv1alpha1.Kind("Garden"), garden.Name, errs) + return nil, apierrors.NewInvalid(operatorv1alpha1.Kind("Garden"), garden.Name, errs) } - return nil + return nil, nil } -func validateUpdate(oldObj, newObj runtime.Object) error { +func validateUpdate(oldObj, newObj runtime.Object) (admission.Warnings, error) { oldGarden, ok := oldObj.(*operatorv1alpha1.Garden) if !ok { - return fmt.Errorf("expected *operatorv1alpha1.Garden but got %T", oldObj) + return nil, fmt.Errorf("expected *operatorv1alpha1.Garden but got %T", oldObj) } newGarden, ok := newObj.(*operatorv1alpha1.Garden) if !ok { - return fmt.Errorf("expected *operatorv1alpha1.Garden but got %T", newObj) + return nil, fmt.Errorf("expected *operatorv1alpha1.Garden but got %T", newObj) } if errs := validation.ValidateGardenUpdate(oldGarden, newGarden); len(errs) > 0 { - return apierrors.NewInvalid(operatorv1alpha1.Kind("Garden"), newGarden.Name, errs) + return nil, apierrors.NewInvalid(operatorv1alpha1.Kind("Garden"), newGarden.Name, errs) } - return nil + return nil, nil } // ValidateCreate performs the validation. -func (h *Handler) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (h *Handler) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { otherGardensAlreadyExist, err := kubernetesutils.ResourcesExist(ctx, h.RuntimeClient, operatorv1alpha1.SchemeGroupVersion.WithKind("GardenList")) if err != nil { - return apierrors.NewInternalError(err) + return nil, apierrors.NewInternalError(err) } if otherGardensAlreadyExist { - return apierrors.NewBadRequest("there can be only one operator.gardener.cloud/v1alpha1.Garden resource in the system at a time") + return nil, apierrors.NewBadRequest("there can be only one operator.gardener.cloud/v1alpha1.Garden resource in the system at a time") } return validate(obj) } // ValidateUpdate performs the validation. -func (h *Handler) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) error { +func (h *Handler) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { return validateUpdate(oldObj, newObj) } // ValidateDelete performs the validation. -func (h *Handler) ValidateDelete(_ context.Context, obj runtime.Object) error { +func (h *Handler) ValidateDelete(_ context.Context, obj runtime.Object) (admission.Warnings, error) { garden, ok := obj.(*operatorv1alpha1.Garden) if !ok { - return fmt.Errorf("expected *operatorv1alpha1.Garden but got %T", obj) + return nil, fmt.Errorf("expected *operatorv1alpha1.Garden but got %T", obj) } - return gardenerutils.CheckIfDeletionIsConfirmed(garden) + return nil, gardenerutils.CheckIfDeletionIsConfirmed(garden) } diff --git a/pkg/operator/webhook/validation/handler_test.go b/pkg/operator/webhook/validation/handler_test.go index ab88563e86b..10a79f2f45d 100644 --- a/pkg/operator/webhook/validation/handler_test.go +++ b/pkg/operator/webhook/validation/handler_test.go @@ -75,7 +75,9 @@ var _ = Describe("Handler", func() { It("should return an error if there are validation errors", func() { metav1.SetMetaDataAnnotation(&garden.ObjectMeta, "gardener.cloud/operation", "rotate-credentials-complete") - err := handler.ValidateCreate(ctx, garden) + warnings, err := handler.ValidateCreate(ctx, garden) + Expect(warnings).To(BeNil()) + statusError, ok := err.(*apierrors.StatusError) Expect(ok).To(BeTrue()) Expect(statusError.Status().Code).To(Equal(int32(http.StatusUnprocessableEntity))) @@ -87,7 +89,9 @@ var _ = Describe("Handler", func() { garden2.SetName("garden2") Expect(fakeClient.Create(ctx, garden2)).To(Succeed()) - err := handler.ValidateCreate(ctx, garden) + warnings, err := handler.ValidateCreate(ctx, garden) + Expect(warnings).To(BeNil()) + statusError, ok := err.(*apierrors.StatusError) Expect(ok).To(BeTrue()) Expect(statusError.Status().Code).To(Equal(int32(http.StatusBadRequest))) @@ -97,14 +101,18 @@ var _ = Describe("Handler", func() { Describe("#ValidateUpdate", func() { It("should return success if there are no errors", func() { - Expect(handler.ValidateUpdate(ctx, garden, garden)).To(Succeed()) + warnings, err := handler.ValidateUpdate(ctx, garden, garden) + Expect(warnings).To(BeNil()) + Expect(err).NotTo(HaveOccurred()) }) It("should return an error if there are validation errors", func() { oldGarden := garden.DeepCopy() metav1.SetMetaDataAnnotation(&garden.ObjectMeta, "gardener.cloud/operation", "rotate-credentials-complete") - err := handler.ValidateUpdate(ctx, oldGarden, garden) + warnings, err := handler.ValidateUpdate(ctx, oldGarden, garden) + Expect(warnings).To(BeNil()) + statusError, ok := err.(*apierrors.StatusError) Expect(ok).To(BeTrue()) Expect(statusError.Status().Code).To(Equal(int32(http.StatusUnprocessableEntity))) @@ -115,7 +123,9 @@ var _ = Describe("Handler", func() { oldGarden := garden.DeepCopy() oldGarden.Spec.VirtualCluster.ControlPlane = &operatorv1alpha1.ControlPlane{HighAvailability: &operatorv1alpha1.HighAvailability{}} - err := handler.ValidateUpdate(ctx, oldGarden, garden) + warnings, err := handler.ValidateUpdate(ctx, oldGarden, garden) + Expect(warnings).To(BeNil()) + statusError, ok := err.(*apierrors.StatusError) Expect(ok).To(BeTrue()) Expect(statusError.Status().Code).To(Equal(int32(http.StatusUnprocessableEntity))) diff --git a/pkg/resourcemanager/webhook/extensionvalidation/handler.go b/pkg/resourcemanager/webhook/extensionvalidation/handler.go index 657d58f85d8..bbf9f665fef 100644 --- a/pkg/resourcemanager/webhook/extensionvalidation/handler.go +++ b/pkg/resourcemanager/webhook/extensionvalidation/handler.go @@ -21,6 +21,7 @@ import ( druidvalidation "github.com/gardener/etcd-druid/api/validation" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" "github.com/gardener/gardener/pkg/apis/extensions/validation" @@ -41,242 +42,242 @@ type ( workerValidator struct{} ) -func (backupBucketValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (backupBucketValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { object := obj.(*extensionsv1alpha1.BackupBucket) if errs := validation.ValidateBackupBucket(object); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.BackupBucketResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.BackupBucketResource), object.GetName(), errs) } - return nil + return nil, nil } -func (backupBucketValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (backupBucketValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { object := newObj.(*extensionsv1alpha1.BackupBucket) if errs := validation.ValidateBackupBucketUpdate(object, oldObj.(*extensionsv1alpha1.BackupBucket)); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.BackupBucketResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.BackupBucketResource), object.GetName(), errs) } - return nil + return nil, nil } -func (backupBucketValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error { - return nil +func (backupBucketValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (backupEntryValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (backupEntryValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { object := obj.(*extensionsv1alpha1.BackupEntry) if errs := validation.ValidateBackupEntry(object); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.BackupEntryResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.BackupEntryResource), object.GetName(), errs) } - return nil + return nil, nil } -func (backupEntryValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (backupEntryValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { object := newObj.(*extensionsv1alpha1.BackupEntry) if errs := validation.ValidateBackupEntryUpdate(object, oldObj.(*extensionsv1alpha1.BackupEntry)); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.BackupEntryResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.BackupEntryResource), object.GetName(), errs) } - return nil + return nil, nil } -func (backupEntryValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error { - return nil +func (backupEntryValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (bastionValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (bastionValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { object := obj.(*extensionsv1alpha1.Bastion) if errs := validation.ValidateBastion(object); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.BastionResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.BastionResource), object.GetName(), errs) } - return nil + return nil, nil } -func (bastionValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (bastionValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { object := newObj.(*extensionsv1alpha1.Bastion) if errs := validation.ValidateBastionUpdate(object, oldObj.(*extensionsv1alpha1.Bastion)); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.BastionResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.BastionResource), object.GetName(), errs) } - return nil + return nil, nil } -func (bastionValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error { - return nil +func (bastionValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (containerRuntimeValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (containerRuntimeValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { object := obj.(*extensionsv1alpha1.ContainerRuntime) if errs := validation.ValidateContainerRuntime(object); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.ContainerRuntimeResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.ContainerRuntimeResource), object.GetName(), errs) } - return nil + return nil, nil } -func (containerRuntimeValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (containerRuntimeValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { object := newObj.(*extensionsv1alpha1.ContainerRuntime) if errs := validation.ValidateContainerRuntimeUpdate(object, oldObj.(*extensionsv1alpha1.ContainerRuntime)); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.ContainerRuntimeResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.ContainerRuntimeResource), object.GetName(), errs) } - return nil + return nil, nil } -func (containerRuntimeValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error { - return nil +func (containerRuntimeValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (controlPlaneValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (controlPlaneValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { object := obj.(*extensionsv1alpha1.ControlPlane) if errs := validation.ValidateControlPlane(object); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.ControlPlaneResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.ControlPlaneResource), object.GetName(), errs) } - return nil + return nil, nil } -func (controlPlaneValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (controlPlaneValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { object := newObj.(*extensionsv1alpha1.ControlPlane) if errs := validation.ValidateControlPlaneUpdate(object, oldObj.(*extensionsv1alpha1.ControlPlane)); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.ControlPlaneResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.ControlPlaneResource), object.GetName(), errs) } - return nil + return nil, nil } -func (controlPlaneValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error { - return nil +func (controlPlaneValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (dnsRecordValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (dnsRecordValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { object := obj.(*extensionsv1alpha1.DNSRecord) if errs := validation.ValidateDNSRecord(object); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.DNSRecordResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.DNSRecordResource), object.GetName(), errs) } - return nil + return nil, nil } -func (dnsRecordValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (dnsRecordValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { object := newObj.(*extensionsv1alpha1.DNSRecord) if errs := validation.ValidateDNSRecordUpdate(object, oldObj.(*extensionsv1alpha1.DNSRecord)); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.DNSRecordResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.DNSRecordResource), object.GetName(), errs) } - return nil + return nil, nil } -func (dnsRecordValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error { - return nil +func (dnsRecordValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (etcdValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (etcdValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { object := obj.(*druidv1alpha1.Etcd) if errs := druidvalidation.ValidateEtcd(object); len(errs) > 0 { - return apierrors.NewInvalid(object.GroupVersionKind().GroupKind(), object.GetName(), errs) + return nil, apierrors.NewInvalid(object.GroupVersionKind().GroupKind(), object.GetName(), errs) } - return nil + return nil, nil } -func (etcdValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (etcdValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { object := newObj.(*druidv1alpha1.Etcd) if errs := druidvalidation.ValidateEtcdUpdate(object, oldObj.(*druidv1alpha1.Etcd)); len(errs) > 0 { - return apierrors.NewInvalid(object.GroupVersionKind().GroupKind(), object.GetName(), errs) + return nil, apierrors.NewInvalid(object.GroupVersionKind().GroupKind(), object.GetName(), errs) } - return nil + return nil, nil } -func (etcdValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error { - return nil +func (etcdValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (extensionValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (extensionValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { object := obj.(*extensionsv1alpha1.Extension) if errs := validation.ValidateExtension(object); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.ExtensionResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.ExtensionResource), object.GetName(), errs) } - return nil + return nil, nil } -func (extensionValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (extensionValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { object := newObj.(*extensionsv1alpha1.Extension) if errs := validation.ValidateExtensionUpdate(object, oldObj.(*extensionsv1alpha1.Extension)); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.ExtensionResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.ExtensionResource), object.GetName(), errs) } - return nil + return nil, nil } -func (extensionValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error { - return nil +func (extensionValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (infrastructureValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (infrastructureValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { object := obj.(*extensionsv1alpha1.Infrastructure) if errs := validation.ValidateInfrastructure(object); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.InfrastructureResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.InfrastructureResource), object.GetName(), errs) } - return nil + return nil, nil } -func (infrastructureValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (infrastructureValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { object := newObj.(*extensionsv1alpha1.Infrastructure) if errs := validation.ValidateInfrastructureUpdate(object, oldObj.(*extensionsv1alpha1.Infrastructure)); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.InfrastructureResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.InfrastructureResource), object.GetName(), errs) } - return nil + return nil, nil } -func (infrastructureValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error { - return nil +func (infrastructureValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (networkValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (networkValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { object := obj.(*extensionsv1alpha1.Network) if errs := validation.ValidateNetwork(object); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.NetworkResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.NetworkResource), object.GetName(), errs) } - return nil + return nil, nil } -func (networkValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (networkValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { object := newObj.(*extensionsv1alpha1.Network) if errs := validation.ValidateNetworkUpdate(object, oldObj.(*extensionsv1alpha1.Network)); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.NetworkResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.NetworkResource), object.GetName(), errs) } - return nil + return nil, nil } -func (networkValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error { - return nil +func (networkValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (operatingSystemConfigValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (operatingSystemConfigValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { object := obj.(*extensionsv1alpha1.OperatingSystemConfig) if errs := validation.ValidateOperatingSystemConfig(object); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.OperatingSystemConfigResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.OperatingSystemConfigResource), object.GetName(), errs) } - return nil + return nil, nil } -func (operatingSystemConfigValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (operatingSystemConfigValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { object := newObj.(*extensionsv1alpha1.OperatingSystemConfig) if errs := validation.ValidateOperatingSystemConfigUpdate(object, oldObj.(*extensionsv1alpha1.OperatingSystemConfig)); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.OperatingSystemConfigResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.OperatingSystemConfigResource), object.GetName(), errs) } - return nil + return nil, nil } -func (operatingSystemConfigValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error { - return nil +func (operatingSystemConfigValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil } -func (workerValidator) ValidateCreate(ctx context.Context, obj runtime.Object) error { +func (workerValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { object := obj.(*extensionsv1alpha1.Worker) if errs := validation.ValidateWorker(object); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.WorkerResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.WorkerResource), object.GetName(), errs) } - return nil + return nil, nil } -func (workerValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) error { +func (workerValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) { object := newObj.(*extensionsv1alpha1.Worker) if errs := validation.ValidateWorkerUpdate(object, oldObj.(*extensionsv1alpha1.Worker)); len(errs) > 0 { - return apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.WorkerResource), object.GetName(), errs) + return nil, apierrors.NewInvalid(extensionsv1alpha1.Kind(extensionsv1alpha1.WorkerResource), object.GetName(), errs) } - return nil + return nil, nil } -func (workerValidator) ValidateDelete(ctx context.Context, obj runtime.Object) error { - return nil +func (workerValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) { + return nil, nil }