diff --git a/api/configuration/v1alpha1/kong_custom_entity_types.go b/api/configuration/v1alpha1/kong_custom_entity_types.go index 0d14a4d..87e6c99 100644 --- a/api/configuration/v1alpha1/kong_custom_entity_types.go +++ b/api/configuration/v1alpha1/kong_custom_entity_types.go @@ -24,7 +24,6 @@ type KongEntityScope string // +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`,description="Age" // +kubebuilder:printcolumn:name="Programmed",type=string,JSONPath=`.status.conditions[?(@.type=="Programmed")].status` // +kubebuilder:validation:XValidation:rule="self.spec.type == oldSelf.spec.type",message="The spec.type field is immutable" -// +kubebuilder:validation:XValidation:rule="!(self.spec.type in ['services','routes','upstreams','targets','plugins','consumers','consumer_groups'])",message="The spec.type field cannot be known Kong entity types" // +apireference:kic:include // +kong:channels=ingress-controller type KongCustomEntity struct { @@ -38,6 +37,7 @@ type KongCustomEntity struct { } // KongCustomEntitySpec defines the specification of the KongCustomEntity. +// +kubebuilder:validation:XValidation:rule="!(self.type in ['services','routes','upstreams','targets','plugins','consumers','consumer_groups'])",message="The type field cannot be one of the known Kong entity types" // +apireference:kic:include type KongCustomEntitySpec struct { // EntityType is the type of the Kong entity. The type is used in generating declarative configuration. @@ -56,11 +56,15 @@ type KongCustomEntitySpec struct { // ObjectReference defines reference of a kubernetes object. // +apireference:kic:include type ObjectReference struct { + // Group defines the API group of the referred object. Group *string `json:"group,omitempty"` - Kind *string `json:"kind,omitempty"` + // Kind defines the kind of the referred object. + // +kubebuilder:validation:Enum=KongPlugin;KongClusterPlugin + Kind *string `json:"kind,omitempty"` // Empty namespace means the same namespace of the owning object. Namespace *string `json:"namespace,omitempty"` - Name string `json:"name"` + // Name defines the name of the referred object. + Name string `json:"name"` } // KongCustomEntityStatus defines the status of the KongCustomEntity. diff --git a/config/crd/ingress-controller/configuration.konghq.com_kongcustomentities.yaml b/config/crd/ingress-controller/configuration.konghq.com_kongcustomentities.yaml index eb2ffbf..25ae59e 100644 --- a/config/crd/ingress-controller/configuration.konghq.com_kongcustomentities.yaml +++ b/config/crd/ingress-controller/configuration.konghq.com_kongcustomentities.yaml @@ -70,10 +70,16 @@ spec: to the entity(service/route/consumer) where the plugin is attached. properties: group: + description: Group defines the API group of the referred object. type: string kind: + description: Kind defines the kind of the referred object. + enum: + - KongPlugin + - KongClusterPlugin type: string name: + description: Name defines the name of the referred object. type: string namespace: description: Empty namespace means the same namespace of the owning @@ -91,6 +97,9 @@ spec: - fields - type type: object + x-kubernetes-validations: + - message: The type field cannot be one of the known Kong entity types + rule: '!(self.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])' status: description: Status stores the reconciling status of the resource. properties: @@ -175,8 +184,6 @@ spec: x-kubernetes-validations: - message: The spec.type field is immutable rule: self.spec.type == oldSelf.spec.type - - message: The spec.type field cannot be known Kong entity types - rule: '!(self.spec.type in [''services'',''routes'',''upstreams'',''targets'',''plugins'',''consumers'',''consumer_groups''])' served: true storage: true subresources: diff --git a/docs/api-reference.md b/docs/api-reference.md index eafedd3..46bc18f 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -1617,10 +1617,10 @@ ObjectReference defines reference of a kubernetes object. | Field | Description | | --- | --- | -| `group` _string_ | | -| `kind` _string_ | | +| `group` _string_ | Group defines the API group of the referred object. | +| `kind` _string_ | Kind defines the kind of the referred object. | | `namespace` _string_ | Empty namespace means the same namespace of the owning object. | -| `name` _string_ | | +| `name` _string_ | Name defines the name of the referred object. | _Appears in:_ diff --git a/test/crdsvalidation/kongcustomentity_test.go b/test/crdsvalidation/kongcustomentity_test.go new file mode 100644 index 0000000..2c3af05 --- /dev/null +++ b/test/crdsvalidation/kongcustomentity_test.go @@ -0,0 +1,226 @@ +package crdsvalidation_test + +import ( + "testing" + + configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1" + "github.com/samber/lo" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +func TestKongCustomEntity(t *testing.T) { + t.Run("spec", func(t *testing.T) { + CRDValidationTestCasesGroup[*configurationv1alpha1.KongCustomEntity]{ + { + Name: "basic allowed spec", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + ParentRef: &configurationv1alpha1.ObjectReference{ + Kind: lo.ToPtr("KongPlugin"), + }, + }, + }, + }, + { + Name: "spec.fields is required", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{}, + }, + ExpectedErrorMessage: lo.ToPtr("spec.fields: Required value"), + }, + { + Name: "spec.type cannot be known Kong entity type - services", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + EntityType: "services", + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("The type field cannot be one of the known Kong entity types"), + }, + { + Name: "spec.type cannot be known Kong entity type - routes", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + EntityType: "routes", + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("The type field cannot be one of the known Kong entity types"), + }, + { + Name: "spec.type cannot be known Kong entity type - upstreams", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + EntityType: "upstreams", + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("The type field cannot be one of the known Kong entity types"), + }, + { + Name: "spec.type cannot be known Kong entity type - targets", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + EntityType: "targets", + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("The type field cannot be one of the known Kong entity types"), + }, + { + Name: "spec.type cannot be known Kong entity type - plugins", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + EntityType: "plugins", + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("The type field cannot be one of the known Kong entity types"), + }, + { + Name: "spec.type cannot be known Kong entity type - consumers", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + EntityType: "consumers", + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("The type field cannot be one of the known Kong entity types"), + }, + { + Name: "spec.type cannot be known Kong entity type - consumer_groups", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + EntityType: "consumer_groups", + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("The type field cannot be one of the known Kong entity types"), + }, + { + Name: "spec.type can be set", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + EntityType: "dummy", + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + }, + }, + }, + { + Name: "spec.type cannot be changed", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + EntityType: "dummy", + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + }, + }, + Update: func(kce *configurationv1alpha1.KongCustomEntity) { + kce.Spec.EntityType = "new-dummy" + }, + ExpectedUpdateErrorMessage: lo.ToPtr("The spec.type field is immutable"), + }, + { + Name: "spec.parentRef.kind KongPlugin is supported", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + ParentRef: &configurationv1alpha1.ObjectReference{ + Kind: lo.ToPtr("KongPlugin"), + }, + }, + }, + }, + { + Name: "spec.parentRef.kind KongClusterPlugin is supported", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + ParentRef: &configurationv1alpha1.ObjectReference{ + Kind: lo.ToPtr("KongClusterPlugin"), + }, + }, + }, + }, + { + Name: "other types for spec.parentRef.kind are not allowed", + TestObject: &configurationv1alpha1.KongCustomEntity{ + ObjectMeta: commonObjectMeta, + Spec: configurationv1alpha1.KongCustomEntitySpec{ + Fields: apiextensionsv1.JSON{ + Raw: []byte( + `{}`, + ), + }, + ParentRef: &configurationv1alpha1.ObjectReference{ + Kind: lo.ToPtr("CustomKind"), + }, + }, + }, + ExpectedErrorMessage: lo.ToPtr("spec.parentRef.kind: Unsupported value: \"CustomKind\": supported values: \"KongPlugin\", \"KongClusterPlugin\""), + }, + }.Run(t) + }) +}