diff --git a/api/configuration/v1alpha1/kongservice_types.go b/api/configuration/v1alpha1/kongservice_types.go index afa9e1a7..ced3d399 100644 --- a/api/configuration/v1alpha1/kongservice_types.go +++ b/api/configuration/v1alpha1/kongservice_types.go @@ -39,6 +39,8 @@ import ( // +kubebuilder:validation:XValidation:rule="!has(oldSelf.spec.controlPlaneRef) || has(self.spec.controlPlaneRef)", message="controlPlaneRef is required once set" // +kubebuilder:validation:XValidation:rule="(!has(self.spec.controlPlaneRef) || !has(self.spec.controlPlaneRef.konnectNamespacedRef)) ? true : !has(self.spec.controlPlaneRef.konnectNamespacedRef.__namespace__)", message="spec.controlPlaneRef cannot specify namespace for namespaced resource" // +kubebuilder:validation:XValidation:rule="(!has(self.spec.controlPlaneRef)) ? true : (!has(self.status) || !self.status.conditions.exists(c, c.type == 'Programmed' && c.status == 'True')) ? true : oldSelf.spec.controlPlaneRef == self.spec.controlPlaneRef", message="spec.controlPlaneRef is immutable when an entity is already Programmed" +// +kubebuilder:validation:XValidation:rule="(!has(self.status) || !self.status.conditions.exists(c, c.type == 'Programmed' && c.status == 'True')) ? true : !has(self.spec.konnect) || !has(self.spec.konnect.adopt) || (has(oldSelf.spec.konnect) && has(oldSelf.spec.konnect.adopt))", message="Cannot add spec.konnect.adopt when an entity is already programmed" +// +kubebuilder:validation:XValidation:rule="(!has(self.spec.konnect) || !has(self.spec.konnect.adopt) || !has(oldSelf.spec.konnect) || !has(oldSelf.spec.konnect.adopt)) ? true : (!has(self.status) || !self.status.conditions.exists(c, c.type == 'Programmed' && c.status == 'True')) ? true : oldSelf.spec.konnect.adopt == self.spec.konnect.adopt", message="spec.konnect.adopt is immutable when an entity is already Programmed" // +apireference:kgo:include // +kong:channels=gateway-operator type KongService struct { @@ -53,12 +55,17 @@ type KongService struct { // KongServiceSpec defines specification of a Kong Service. // +kubebuilder:validation:XValidation:rule="!has(self.controlPlaneRef) ? true : self.controlPlaneRef.type != 'kic'", message="KIC is not supported as control plane" +// +kubebuilder:validation:XValidation:rule="(has(self.controlPlaneRef) && (self.controlPlaneRef.type == 'konnectNamespacedRef' || self.controlPlaneRef.type == 'konnectID')) ? true : !has(self.konnect)", message="Cannot specify Konnect specific options when control plane is not Konnect" // +apireference:kgo:include type KongServiceSpec struct { // ControlPlaneRef is a reference to a ControlPlane this KongService is associated with. // +kubebuilder:validation:Required ControlPlaneRef *ControlPlaneRef `json:"controlPlaneRef"` + // KonnectOptions includes options specific to Konnect. + // Only used when we manage the KongService by Konnect entity controller. + KonnectOptions *konnectv1alpha1.KonnectEntityOptions `json:"konnect,omitempty"` + KongServiceAPISpec `json:",inline"` } diff --git a/api/configuration/v1alpha1/zz_generated.deepcopy.go b/api/configuration/v1alpha1/zz_generated.deepcopy.go index 7e8fd5be..268b55f2 100644 --- a/api/configuration/v1alpha1/zz_generated.deepcopy.go +++ b/api/configuration/v1alpha1/zz_generated.deepcopy.go @@ -2350,6 +2350,11 @@ func (in *KongServiceSpec) DeepCopyInto(out *KongServiceSpec) { *out = new(ControlPlaneRef) (*in).DeepCopyInto(*out) } + if in.KonnectOptions != nil { + in, out := &in.KonnectOptions, &out.KonnectOptions + *out = new(konnectv1alpha1.KonnectEntityOptions) + (*in).DeepCopyInto(*out) + } in.KongServiceAPISpec.DeepCopyInto(&out.KongServiceAPISpec) } diff --git a/api/konnect/v1alpha1/konnect_configuration.go b/api/konnect/v1alpha1/konnect_configuration.go index f5e05975..82600d94 100644 --- a/api/konnect/v1alpha1/konnect_configuration.go +++ b/api/konnect/v1alpha1/konnect_configuration.go @@ -14,3 +14,15 @@ type KonnectConfiguration struct { // This is a good place to add fields like "class" which could reference a cluster-wide // configuration for Konnect (similar to what Gateway API's GatewayClass). } + +// KonnectEntityOptions stores the options of entities specific to Konnect. +// +apireference:kgo:include +type KonnectEntityOptions struct { + Adopt *KonnectAdoptOptions `json:"adopt,omitempty"` +} + +// KonnectAdoptOptions stores the options for adopting existing Konnect entities. +// +apireference:kgo:include +type KonnectAdoptOptions struct { + ID string `json:"id"` +} diff --git a/api/konnect/v1alpha1/konnect_gateway_controlplane_types.go b/api/konnect/v1alpha1/konnect_gateway_controlplane_types.go index cc8517c9..5c4e08f8 100644 --- a/api/konnect/v1alpha1/konnect_gateway_controlplane_types.go +++ b/api/konnect/v1alpha1/konnect_gateway_controlplane_types.go @@ -23,6 +23,8 @@ func init() { // +kubebuilder:printcolumn:name="OrgID",description="Konnect Organization ID this resource belongs to.",type=string,JSONPath=`.status.organizationID` // +kubebuilder:validation:XValidation:message="spec.konnect.authRef is immutable when an entity is already Programmed", rule="!self.status.conditions.exists(c, c.type == 'Programmed' && c.status == 'True') ? true : self.spec.konnect.authRef == oldSelf.spec.konnect.authRef" // +kubebuilder:validation:XValidation:message="spec.konnect.authRef is immutable when an entity refers to a Valid API Auth Configuration", rule="!self.status.conditions.exists(c, c.type == 'APIAuthValid' && c.status == 'True') ? true : self.spec.konnect.authRef == oldSelf.spec.konnect.authRef" +// +kubebuilder:validation:XValidation:message="Cannot add spec.adopt when an entitiy is already Programmed", rule="!self.status.conditions.exists(c, c.type == 'Programmed' && c.status == 'True') ? true : (!has(self.spec.adopt) || has(oldSelf.spec.adopt))" +// +kubebuilder:validation:XValidation:message="spec.adopt is immutable when an entitiy is already Programmed", rule="!self.status.conditions.exists(c, c.type == 'Programmed' && c.status == 'True') ? true : ((!has(self.spec.adopt) || !has(oldSelf.spec.adopt)) ? true : self.spec.adopt == oldSelf.spec.adopt)" // +apireference:kgo:include // +kong:channels=gateway-operator type KonnectGatewayControlPlane struct { @@ -53,6 +55,8 @@ type KonnectGatewayControlPlaneSpec struct { // Only applicable for ControlPlanes that are created as groups. Members []corev1.LocalObjectReference `json:"members,omitempty"` + Adopt *KonnectAdoptOptions `json:"adopt,omitempty"` + KonnectConfiguration KonnectConfiguration `json:"konnect,omitempty"` } diff --git a/api/konnect/v1alpha1/zz_generated.deepcopy.go b/api/konnect/v1alpha1/zz_generated.deepcopy.go index cd77a0b5..7f364f83 100644 --- a/api/konnect/v1alpha1/zz_generated.deepcopy.go +++ b/api/konnect/v1alpha1/zz_generated.deepcopy.go @@ -142,6 +142,41 @@ func (in *KonnectAPIAuthConfigurationStatus) DeepCopy() *KonnectAPIAuthConfigura return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KonnectAdoptOptions) DeepCopyInto(out *KonnectAdoptOptions) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KonnectAdoptOptions. +func (in *KonnectAdoptOptions) DeepCopy() *KonnectAdoptOptions { + if in == nil { + return nil + } + out := new(KonnectAdoptOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KonnectEntityOptions) DeepCopyInto(out *KonnectEntityOptions) { + *out = *in + if in.Adopt != nil { + in, out := &in.Adopt, &out.Adopt + *out = new(KonnectAdoptOptions) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KonnectEntityOptions. +func (in *KonnectEntityOptions) DeepCopy() *KonnectEntityOptions { + if in == nil { + return nil + } + out := new(KonnectEntityOptions) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KonnectEntityStatus) DeepCopyInto(out *KonnectEntityStatus) { *out = *in @@ -321,6 +356,11 @@ func (in *KonnectGatewayControlPlaneSpec) DeepCopyInto(out *KonnectGatewayContro *out = make([]v1.LocalObjectReference, len(*in)) copy(*out, *in) } + if in.Adopt != nil { + in, out := &in.Adopt, &out.Adopt + *out = new(KonnectAdoptOptions) + **out = **in + } out.KonnectConfiguration = in.KonnectConfiguration } diff --git a/config/crd/gateway-operator/configuration.konghq.com_kongservices.yaml b/config/crd/gateway-operator/configuration.konghq.com_kongservices.yaml index 90fdd6c2..aa17e6c8 100644 --- a/config/crd/gateway-operator/configuration.konghq.com_kongservices.yaml +++ b/config/crd/gateway-operator/configuration.konghq.com_kongservices.yaml @@ -135,6 +135,21 @@ spec: description: The host of the upstream server. Note that the host value is case sensitive. type: string + konnect: + description: |- + KonnectOptions includes options specific to Konnect. + Only used when we manage the KongService by Konnect entity controller. + properties: + adopt: + description: KonnectAdoptOptions stores the options for adopting + existing Konnect entities. + properties: + id: + type: string + required: + - id + type: object + type: object name: description: The Service name. type: string @@ -193,6 +208,10 @@ spec: - message: KIC is not supported as control plane rule: '!has(self.controlPlaneRef) ? true : self.controlPlaneRef.type != ''kic''' + - message: Cannot specify Konnect specific options when control plane + is not Konnect + rule: '(has(self.controlPlaneRef) && (self.controlPlaneRef.type == ''konnectNamespacedRef'' + || self.controlPlaneRef.type == ''konnectID'')) ? true : !has(self.konnect)' status: default: conditions: @@ -301,6 +320,15 @@ spec: rule: '(!has(self.spec.controlPlaneRef)) ? true : (!has(self.status) || !self.status.conditions.exists(c, c.type == ''Programmed'' && c.status == ''True'')) ? true : oldSelf.spec.controlPlaneRef == self.spec.controlPlaneRef' + - message: Cannot add spec.konnect.adopt when an entity is already programmed + rule: '(!has(self.status) || !self.status.conditions.exists(c, c.type == + ''Programmed'' && c.status == ''True'')) ? true : !has(self.spec.konnect) + || !has(self.spec.konnect.adopt) || (has(oldSelf.spec.konnect) && has(oldSelf.spec.konnect.adopt))' + - message: spec.konnect.adopt is immutable when an entity is already Programmed + rule: '(!has(self.spec.konnect) || !has(self.spec.konnect.adopt) || !has(oldSelf.spec.konnect) + || !has(oldSelf.spec.konnect.adopt)) ? true : (!has(self.status) || !self.status.conditions.exists(c, + c.type == ''Programmed'' && c.status == ''True'')) ? true : oldSelf.spec.konnect.adopt + == self.spec.konnect.adopt' served: true storage: true subresources: diff --git a/config/crd/gateway-operator/konnect.konghq.com_konnectgatewaycontrolplanes.yaml b/config/crd/gateway-operator/konnect.konghq.com_konnectgatewaycontrolplanes.yaml index c3cb8259..58a55802 100644 --- a/config/crd/gateway-operator/konnect.konghq.com_konnectgatewaycontrolplanes.yaml +++ b/config/crd/gateway-operator/konnect.konghq.com_konnectgatewaycontrolplanes.yaml @@ -54,6 +54,15 @@ spec: spec: description: Spec defines the desired state of KonnectGatewayControlPlane. properties: + adopt: + description: KonnectAdoptOptions stores the options for adopting existing + Konnect entities. + properties: + id: + type: string + required: + - id + type: object auth_type: description: The auth type value of the cluster associated with the Runtime Group. @@ -274,6 +283,13 @@ spec: API Auth Configuration rule: '!self.status.conditions.exists(c, c.type == ''APIAuthValid'' && c.status == ''True'') ? true : self.spec.konnect.authRef == oldSelf.spec.konnect.authRef' + - message: Cannot add spec.adopt when an entitiy is already Programmed + rule: '!self.status.conditions.exists(c, c.type == ''Programmed'' && c.status + == ''True'') ? true : (!has(self.spec.adopt) || has(oldSelf.spec.adopt))' + - message: spec.adopt is immutable when an entitiy is already Programmed + rule: '!self.status.conditions.exists(c, c.type == ''Programmed'' && c.status + == ''True'') ? true : ((!has(self.spec.adopt) || !has(oldSelf.spec.adopt)) + ? true : self.spec.adopt == oldSelf.spec.adopt)' served: true storage: true subresources: diff --git a/docs/api-reference.md b/docs/api-reference.md index 0b8a531d..4bcac659 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -1435,6 +1435,7 @@ KongServiceSpec defines specification of a Kong Service. | Field | Description | | --- | --- | | `controlPlaneRef` _[ControlPlaneRef](#controlplaneref)_ | ControlPlaneRef is a reference to a ControlPlane this KongService is associated with. | +| `konnect` _[KonnectEntityOptions](#konnectentityoptions)_ | KonnectOptions includes options specific to Konnect. Only used when we manage the KongService by Konnect entity controller. | | `url` _string_ | Helper field to set `protocol`, `host`, `port` and `path` using a URL. This field is write-only and is not returned in responses. | | `connect_timeout` _integer_ | The timeout in milliseconds for establishing a connection to the upstream server. | | `enabled` _boolean_ | Whether the Service is active. If set to `false`, the proxy behavior will be as if any routes attached to it do not exist (404). Default: `true`. | diff --git a/docs/konnect-api-reference.md b/docs/konnect-api-reference.md index 03e7fafa..acfa19de 100644 --- a/docs/konnect-api-reference.md +++ b/docs/konnect-api-reference.md @@ -92,6 +92,22 @@ KonnectAPIAuthType is the type of authentication used to authenticate with the K _Appears in:_ - [KonnectAPIAuthConfigurationSpec](#konnectapiauthconfigurationspec) +#### KonnectAdoptOptions + + +KonnectAdoptOptions stores the options for adopting existing Konnect entities. + + + +| Field | Description | +| --- | --- | +| `id` _string_ | | + + +_Appears in:_ +- [KonnectEntityOptions](#konnectentityoptions) +- [KonnectGatewayControlPlaneSpec](#konnectgatewaycontrolplanespec) + #### KonnectConfiguration @@ -107,6 +123,8 @@ KonnectConfiguration is the Schema for the KonnectConfiguration API. _Appears in:_ - [KonnectGatewayControlPlaneSpec](#konnectgatewaycontrolplanespec) + + #### KonnectEntityStatus @@ -159,6 +177,7 @@ KonnectGatewayControlPlaneSpec defines the desired state of KonnectGatewayContro | `proxy_urls` _[ProxyURL](#proxyurl) array_ | Array of proxy URLs associated with reaching the data-planes connected to a control-plane. | | `labels` _object (keys:string, values:string)_ | Labels store metadata of an entity that can be used for filtering an entity list or for searching across entity types.

Keys must be of length 1-63 characters, and cannot start with "kong", "konnect", "mesh", "kic", or "_". | | `members` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#localobjectreference-v1-core) array_ | Members is a list of references to the KonnectGatewayControlPlaneMembers that are part of this control plane group. Only applicable for ControlPlanes that are created as groups. | +| `adopt` _[KonnectAdoptOptions](#konnectadoptoptions)_ | | | `konnect` _[KonnectConfiguration](#konnectconfiguration)_ | | diff --git a/test/crdsvalidation/kongservice_test.go b/test/crdsvalidation/kongservice_test.go index f320ecb3..43ea0b04 100644 --- a/test/crdsvalidation/kongservice_test.go +++ b/test/crdsvalidation/kongservice_test.go @@ -8,6 +8,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + konnectv1alpha1 "github.com/kong/kubernetes-configuration/api/konnect/v1alpha1" "github.com/kong/kubernetes-configuration/test/crdsvalidation" ) @@ -28,6 +29,168 @@ func TestKongService(t *testing.T) { NewCRDValidationTestCasesGroupCPRefChangeKICUnsupportedTypes(t, obj, EmptyControlPlaneRefNotAllowed).Run(t) }) + t.Run("konnect adopt", func(t *testing.T) { + crdsvalidation.TestCasesGroup[*configurationv1alpha1.KongService]{ + { + Name: "konnect adopt can be changed before getting programmed", + TestObject: &configurationv1alpha1.KongService{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongServiceSpec{ + ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, + KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ + Name: "test-konnect-control-plane", + }, + }, + KonnectOptions: &konnectv1alpha1.KonnectEntityOptions{ + Adopt: &konnectv1alpha1.KonnectAdoptOptions{ + ID: "abcddcba-0000-1111-9999-0123456789ab", + }, + }, + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Host: "example.com", + }, + }, + }, + Update: func(ks *configurationv1alpha1.KongService) { + ks.Spec.KonnectOptions = &konnectv1alpha1.KonnectEntityOptions{ + Adopt: &konnectv1alpha1.KonnectAdoptOptions{ + ID: "abcddcba-0000-1111-9999-0123456789ac", + }, + } + }, + }, + { + Name: "konnect adopt cannot be changed after programmed", + TestObject: &configurationv1alpha1.KongService{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongServiceSpec{ + ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, + KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ + Name: "test-konnect-control-plane", + }, + }, + KonnectOptions: &konnectv1alpha1.KonnectEntityOptions{ + Adopt: &konnectv1alpha1.KonnectAdoptOptions{ + ID: "abcddcba-0000-1111-9999-0123456789ab", + }, + }, + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Host: "example.com", + }, + }, + Status: configurationv1alpha1.KongServiceStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{ + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "abcddcba-0000-1111-9999-0123456789ac", + }, + ControlPlaneID: "cp-1", + }, + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + Update: func(ks *configurationv1alpha1.KongService) { + ks.Spec.KonnectOptions = &konnectv1alpha1.KonnectEntityOptions{ + Adopt: &konnectv1alpha1.KonnectAdoptOptions{ + ID: "abcddcba-0000-1111-9999-fd9876543211", + }, + } + }, + ExpectedUpdateErrorMessage: lo.ToPtr("spec.konnect.adopt is immutable when an entity is already Programmed"), + }, + { + Name: "Cannot set konnect adopt after programmed", + TestObject: &configurationv1alpha1.KongService{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongServiceSpec{ + ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, + KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ + Name: "test-konnect-control-plane", + }, + }, + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Host: "example.com", + }, + }, + Status: configurationv1alpha1.KongServiceStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{ + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "abcddcba-0000-1111-9999-fdecba987654", + }, + ControlPlaneID: "cp-1", + }, + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + Update: func(ks *configurationv1alpha1.KongService) { + ks.Spec.KonnectOptions = &konnectv1alpha1.KonnectEntityOptions{ + Adopt: &konnectv1alpha1.KonnectAdoptOptions{ + ID: "abcddcba-0000-1111-9999-1234567890ab", + }, + } + }, + ExpectedUpdateErrorMessage: lo.ToPtr("Cannot add spec.konnect.adopt when an entity is already programmed"), + }, + { + Name: "konnect adopt can be removed after programmed", + TestObject: &configurationv1alpha1.KongService{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongServiceSpec{ + ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{ + Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef, + KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{ + Name: "test-konnect-control-plane", + }, + }, + KonnectOptions: &konnectv1alpha1.KonnectEntityOptions{ + Adopt: &konnectv1alpha1.KonnectAdoptOptions{ + ID: "abcddcba-0000-1111-9999-0123456789ab", + }, + }, + KongServiceAPISpec: configurationv1alpha1.KongServiceAPISpec{ + Host: "example.com", + }, + }, + Status: configurationv1alpha1.KongServiceStatus{ + Konnect: &konnectv1alpha1.KonnectEntityStatusWithControlPlaneRef{ + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "abcddcba-0000-1111-9999-fdecba987654", + }, + ControlPlaneID: "cp-1", + }, + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Valid", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + Update: func(ks *configurationv1alpha1.KongService) { + ks.Spec.KonnectOptions.Adopt = nil + }, + }, + }.Run(t) + }) + t.Run("tags validation", func(t *testing.T) { crdsvalidation.TestCasesGroup[*configurationv1alpha1.KongService]{ { diff --git a/test/crdsvalidation/konnectgatewaycontrolplane_test.go b/test/crdsvalidation/konnectgatewaycontrolplane_test.go index 579eae79..4cb75db1 100644 --- a/test/crdsvalidation/konnectgatewaycontrolplane_test.go +++ b/test/crdsvalidation/konnectgatewaycontrolplane_test.go @@ -685,4 +685,155 @@ func TestKonnectGatewayControlPlane(t *testing.T) { }, }.Run(t) }) + + t.Run("constraint on adopt options", func(t *testing.T) { + crdsvalidation.TestCasesGroup[*konnectv1alpha1.KonnectGatewayControlPlane]{ + { + Name: "Can update adopt options before programmed", + TestObject: &konnectv1alpha1.KonnectGatewayControlPlane{ + ObjectMeta: commonObjectMeta, + Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{ + Adopt: &konnectv1alpha1.KonnectAdoptOptions{ + ID: "abcddcba-1234-5678-abcd-0123456789ab", + }, + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ + Name: "cp-1", + ClusterType: sdkkonnectcomp.CreateControlPlaneRequestClusterTypeClusterTypeControlPlane.ToPointer(), + }, + KonnectConfiguration: konnectv1alpha1.KonnectConfiguration{ + APIAuthConfigurationRef: konnectv1alpha1.KonnectAPIAuthConfigurationRef{ + Name: "name-1", + }, + }, + }, + Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{ + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: metav1.ConditionUnknown, + Reason: "Pending", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + Update: func(kgcp *konnectv1alpha1.KonnectGatewayControlPlane) { + kgcp.Spec.Adopt = &konnectv1alpha1.KonnectAdoptOptions{ + ID: "abcddcba-1234-5678-abcd-0123456789ac", + } + }, + }, + { + Name: "Cannot update adopt options after programmed", + TestObject: &konnectv1alpha1.KonnectGatewayControlPlane{ + ObjectMeta: commonObjectMeta, + Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{ + Adopt: &konnectv1alpha1.KonnectAdoptOptions{ + ID: "abcddcba-1234-5678-abcd-0123456789ab", + }, + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ + Name: "cp-1", + ClusterType: sdkkonnectcomp.CreateControlPlaneRequestClusterTypeClusterTypeControlPlane.ToPointer(), + }, + KonnectConfiguration: konnectv1alpha1.KonnectConfiguration{ + APIAuthConfigurationRef: konnectv1alpha1.KonnectAPIAuthConfigurationRef{ + Name: "name-1", + }, + }, + }, + Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{ + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "abcddcba-1234-5678-abcd-0123456789ab", + }, + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Programmed", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + Update: func(kgcp *konnectv1alpha1.KonnectGatewayControlPlane) { + kgcp.Spec.Adopt = &konnectv1alpha1.KonnectAdoptOptions{ + ID: "abcddcba-1234-5678-abcd-0123456789ac", + } + }, + ExpectedUpdateErrorMessage: lo.ToPtr("spec.adopt is immutable when an entitiy is already Programmed"), + }, + { + Name: "Cannot add adopt options after programmed", + TestObject: &konnectv1alpha1.KonnectGatewayControlPlane{ + ObjectMeta: commonObjectMeta, + Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{ + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ + Name: "cp-1", + ClusterType: sdkkonnectcomp.CreateControlPlaneRequestClusterTypeClusterTypeControlPlane.ToPointer(), + }, + KonnectConfiguration: konnectv1alpha1.KonnectConfiguration{ + APIAuthConfigurationRef: konnectv1alpha1.KonnectAPIAuthConfigurationRef{ + Name: "name-1", + }, + }, + }, + Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{ + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "abcddcba-1234-5678-abcd-0123456789ab", + }, + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Programmed", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + Update: func(kgcp *konnectv1alpha1.KonnectGatewayControlPlane) { + kgcp.Spec.Adopt = &konnectv1alpha1.KonnectAdoptOptions{ + ID: "abcddcba-1234-5678-abcd-0123456789ac", + } + }, + ExpectedUpdateErrorMessage: lo.ToPtr("Cannot add spec.adopt when an entitiy is already Programmed"), + }, + { + Name: "can delete adopt options after programmed", + TestObject: &konnectv1alpha1.KonnectGatewayControlPlane{ + ObjectMeta: commonObjectMeta, + Spec: konnectv1alpha1.KonnectGatewayControlPlaneSpec{ + Adopt: &konnectv1alpha1.KonnectAdoptOptions{ + ID: "abcddcba-1234-5678-abcd-0123456789ab", + }, + CreateControlPlaneRequest: sdkkonnectcomp.CreateControlPlaneRequest{ + Name: "cp-1", + ClusterType: sdkkonnectcomp.CreateControlPlaneRequestClusterTypeClusterTypeControlPlane.ToPointer(), + }, + KonnectConfiguration: konnectv1alpha1.KonnectConfiguration{ + APIAuthConfigurationRef: konnectv1alpha1.KonnectAPIAuthConfigurationRef{ + Name: "name-1", + }, + }, + }, + Status: konnectv1alpha1.KonnectGatewayControlPlaneStatus{ + KonnectEntityStatus: konnectv1alpha1.KonnectEntityStatus{ + ID: "abcddcba-1234-5678-abcd-0123456789ab", + }, + Conditions: []metav1.Condition{ + { + Type: "Programmed", + Status: metav1.ConditionTrue, + Reason: "Programmed", + LastTransitionTime: metav1.Now(), + }, + }, + }, + }, + Update: func(kgcp *konnectv1alpha1.KonnectGatewayControlPlane) { + kgcp.Spec.Adopt = nil + }, + }, + }.Run(t) + }) }