@@ -24,6 +24,9 @@ import (
2424// See https://book.kubebuilder.io/reference/markers/webhook for docs
2525//+kubebuilder:webhook:verbs=create;update;delete,path=/validate-vshn-appcat-vshn-io-v1-vshnpostgresql,mutating=false,failurePolicy=fail,groups=vshn.appcat.vshn.io,resources=vshnpostgresqls,versions=v1,name=postgresql.vshn.appcat.vshn.io,sideEffects=None,admissionReviewVersions=v1
2626
27+ // Protect the XVSHNPostgreSQL composite from having its compositionRef changed once set.
28+ //+kubebuilder:webhook:verbs=update,path=/validate-vshn-appcat-vshn-io-v1-xvshnpostgresql,mutating=false,failurePolicy=fail,groups=vshn.appcat.vshn.io,resources=xvshnpostgresqls,versions=v1,name=xvshnpostgresql.vshn.appcat.vshn.io,sideEffects=None,admissionReviewVersions=v1
29+
2730//RBAC
2831//+kubebuilder:rbac:groups=vshn.appcat.vshn.io,resources=xvshnpostgresqls,verbs=get;list;watch;patch;update
2932//+kubebuilder:rbac:groups=vshn.appcat.vshn.io,resources=xvshnpostgresqls/status,verbs=get;list;watch;patch;update
@@ -38,10 +41,12 @@ const (
3841)
3942
4043var (
41- pgGK = schema.GroupKind {Group : "vshn.appcat.vshn.io" , Kind : "VSHNPostgreSQL" }
42- pgGR = schema.GroupResource {Group : pgGK .Group , Resource : "vshnpostgresqls" }
44+ pgGK = schema.GroupKind {Group : "vshn.appcat.vshn.io" , Kind : "VSHNPostgreSQL" }
45+ pgGR = schema.GroupResource {Group : pgGK .Group , Resource : "vshnpostgresqls" }
46+ xpgGK = schema.GroupKind {Group : "vshn.appcat.vshn.io" , Kind : "XVSHNPostgreSQL" }
4347
4448 _ webhook.CustomValidator = & PostgreSQLWebhookHandler {}
49+ _ webhook.CustomValidator = & XVSHNPostgreSQLWebhookHandler {}
4550
4651 blocklist = map [string ]string {
4752 "listen_addresses" : "" ,
@@ -84,6 +89,46 @@ func SetupPostgreSQLWebhookHandlerWithManager(mgr ctrl.Manager, withQuota bool)
8489 Complete ()
8590}
8691
92+ // XVSHNPostgreSQLWebhookHandler validates the nested XVSHNPostgreSQL composite resource.
93+ type XVSHNPostgreSQLWebhookHandler struct {}
94+
95+ // SetupXVSHNPostgreSQLWebhookHandlerWithManager registers the XVSHNPostgreSQL validation webhook.
96+ func SetupXVSHNPostgreSQLWebhookHandlerWithManager (mgr ctrl.Manager ) error {
97+ return ctrl .NewWebhookManagedBy (mgr ).
98+ For (& vshnv1.XVSHNPostgreSQL {}).
99+ WithValidator (& XVSHNPostgreSQLWebhookHandler {}).
100+ Complete ()
101+ }
102+
103+ func (x * XVSHNPostgreSQLWebhookHandler ) ValidateCreate (_ context.Context , _ runtime.Object ) (admission.Warnings , error ) {
104+ return nil , nil
105+ }
106+
107+ func (x * XVSHNPostgreSQLWebhookHandler ) ValidateUpdate (_ context.Context , oldObj , newObj runtime.Object ) (admission.Warnings , error ) {
108+ oldPg , ok := oldObj .(* vshnv1.XVSHNPostgreSQL )
109+ if ! ok {
110+ return nil , fmt .Errorf ("provided manifest is not a valid XVSHNPostgreSQL object" )
111+ }
112+ newPg , ok := newObj .(* vshnv1.XVSHNPostgreSQL )
113+ if ! ok {
114+ return nil , fmt .Errorf ("provided manifest is not a valid XVSHNPostgreSQL object" )
115+ }
116+
117+ if newPg .DeletionTimestamp != nil {
118+ return nil , nil
119+ }
120+
121+ allErrs := newFielErrors (newPg .Name , xpgGK )
122+ if err := validateCompositionRefImmutability (oldPg .Spec .CompositionRef .Name , newPg .Spec .CompositionRef .Name ); err != nil {
123+ allErrs .Add (err )
124+ }
125+ return nil , allErrs .Get ()
126+ }
127+
128+ func (x * XVSHNPostgreSQLWebhookHandler ) ValidateDelete (_ context.Context , _ runtime.Object ) (admission.Warnings , error ) {
129+ return nil , nil
130+ }
131+
87132func (p * PostgreSQLWebhookHandler ) ValidateCreate (ctx context.Context , obj runtime.Object ) (admission.Warnings , error ) {
88133 return p .validatePostgreSQL (ctx , obj , nil , true )
89134}
@@ -151,8 +196,8 @@ func (p *PostgreSQLWebhookHandler) validatePostgreSQL(ctx context.Context, newOb
151196
152197 // Do not allow changing compositionRef if it has been set previously.
153198 // When creating a new VSHNPostgresQL, crossplane will automatically set this field if unset.
154- if oldPg .Spec .CompositionRef .Name != "" && newPg .Spec .CompositionRef .Name != oldPg . Spec . CompositionRef . Name {
155- allErrs .Add (field . Forbidden ( field . NewPath ( "spec" , "compositionRef" ), "compositionRef is immutable" ) )
199+ if err := validateCompositionRefImmutability ( oldPg .Spec .CompositionRef .Name , newPg .Spec .CompositionRef .Name ); err != nil {
200+ allErrs .Add (err )
156201 }
157202
158203 // Check for disk downsizing
@@ -389,6 +434,14 @@ func validatePostgreSQLEncryptionChanges(newEncryption, oldEncryption *vshnv1.VS
389434 return nil
390435}
391436
437+ // validateCompositionRefImmutability returns a Forbidden error if the compositionRef name has changed after being set
438+ func validateCompositionRefImmutability (oldRef , newRef string ) * field.Error {
439+ if oldRef != "" && newRef != oldRef {
440+ return field .Forbidden (field .NewPath ("spec" , "compositionRef" ), "compositionRef is immutable" )
441+ }
442+ return nil
443+ }
444+
392445// validatePinImageTag validates that pinImageTag's major version matches the specified majorVersion
393446func validatePinImageTag (pinImageTag , majorVersion string ) * field.Error {
394447 if pinImageTag == "" {
0 commit comments