Skip to content

Commit

Permalink
feat: add KongCustomEntity's spec.parentRef.kind validation (#180)
Browse files Browse the repository at this point in the history
  • Loading branch information
pmalek authored Dec 6, 2024
1 parent 8f91f38 commit 19ed9b4
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 8 deletions.
10 changes: 7 additions & 3 deletions api/configuration/v1alpha1/kong_custom_entity_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
Expand All @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:_
Expand Down
226 changes: 226 additions & 0 deletions test/crdsvalidation/kongcustomentity_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}

0 comments on commit 19ed9b4

Please sign in to comment.