diff --git a/internal/webhook/management_webhook.go b/internal/webhook/management_webhook.go index 1c008fa11..0c70153cd 100644 --- a/internal/webhook/management_webhook.go +++ b/internal/webhook/management_webhook.go @@ -34,7 +34,8 @@ type ManagementValidator struct { } var ( - ErrManagementDeletionForbidden = errors.New("management deletion is forbidden") + ErrManagementDeletionForbidden = errors.New("management deletion is forbidden") + ErrCreateManagementReenablingForbidden = errors.New("reenabling of the createManagement parameter is forbidden") ) func (in *ManagementValidator) SetupWebhookWithManager(mgr ctrl.Manager) error { @@ -57,7 +58,35 @@ func (*ManagementValidator) ValidateCreate(_ context.Context, _ runtime.Object) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type. -func (*ManagementValidator) ValidateUpdate(_ context.Context, _ runtime.Object, _ runtime.Object) (admission.Warnings, error) { +func (*ManagementValidator) ValidateUpdate(_ context.Context, _ runtime.Object, newObj runtime.Object) (admission.Warnings, error) { + // Ensure the newObj is of the expected type. + newManagement, ok := newObj.(*v1alpha1.Management) + if !ok { + return nil, apierrors.NewBadRequest(fmt.Sprintf("expected Management but got a %T", newObj)) + } + + // without Core, no further validation is needed. + if newManagement.Spec.Core == nil { + return nil, nil + } + + // Retrieve Helm values. + vals, err := newManagement.Spec.Core.HMC.HelmValues() + if err != nil { + return nil, apierrors.NewBadRequest(fmt.Sprintf("cannot retrieve helm values: %v", err)) + } + + // Check if the 'controller' field exists and is a map. + controller, ok := vals["controller"].(map[string]interface{}) + if !ok { + return nil, nil + } + + // Check if 'createManagement' exists and is true. + if createManagement, ok := controller["createManagement"].(bool); ok && createManagement { + return nil, ErrCreateManagementReenablingForbidden + } + return nil, nil } diff --git a/internal/webhook/management_webhook_test.go b/internal/webhook/management_webhook_test.go index a8bd18036..8e2582792 100644 --- a/internal/webhook/management_webhook_test.go +++ b/internal/webhook/management_webhook_test.go @@ -16,9 +16,11 @@ package webhook import ( "context" + "encoding/json" "testing" . "github.com/onsi/gomega" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" @@ -75,3 +77,78 @@ func TestManagementValidateDelete(t *testing.T) { }) } } + +func getManagementObjWithCreateFlag(flag bool) (*v1alpha1.Management, error) { + hmcConfig := map[string]interface{}{ + "controller": map[string]interface{}{ + "createManagement": flag, + }, + } + + rawConfig, err := json.Marshal(hmcConfig) + if err != nil { + return nil, err + } + + mgmtObj := &v1alpha1.Management{ + Spec: v1alpha1.ManagementSpec{ + Core: &v1alpha1.Core{ + HMC: v1alpha1.Component{ + Config: &apiextensionsv1.JSON{ + Raw: rawConfig, + }, + }, + }, + }, + } + + return mgmtObj, nil +} + +func TestManagementValidateUpdate(t *testing.T) { + g := NewWithT(t) + + ctx := context.Background() + + objToFail, err := getManagementObjWithCreateFlag(true) + g.Expect(err).To(Succeed()) + + objToSucceed, err := getManagementObjWithCreateFlag(false) + g.Expect(err).To(Succeed()) + + tests := []struct { + name string + management *v1alpha1.Management + err string + }{ + { + name: "should fail if Management object has controller.createManagement=true", + management: objToFail, + err: "reenabling of the createManagement parameter is forbidden", + }, + { + name: "should succeed for controller.createManagement=false", + management: objToSucceed, + }, + { + name: "should succeed", + management: management.NewManagement(), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() + validator := &ManagementValidator{Client: c} + _, err := validator.ValidateUpdate(ctx, management.NewManagement(), tt.management) + if tt.err != "" { + g.Expect(err).To(HaveOccurred()) + if err.Error() != tt.err { + t.Fatalf("expected error '%s', got error: %s", tt.err, err.Error()) + } + } else { + g.Expect(err).To(Succeed()) + } + }) + } +}