Skip to content

Commit

Permalink
feat(konnect): allow sericeless KongRoutes (#101)
Browse files Browse the repository at this point in the history
  • Loading branch information
pmalek authored Sep 27, 2024
1 parent e86cc14 commit 68e4bb7
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 8 deletions.
14 changes: 12 additions & 2 deletions api/configuration/v1alpha1/kongroute_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@ import (
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Programmed",description="The Resource is Programmed on Konnect",type=string,JSONPath=`.status.conditions[?(@.type=='Programmed')].status`
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.spec.serviceRef) || has(self.spec.serviceRef)", message="serviceRef is required once set"
// +kubebuilder:validation:XValidation:rule="(!self.status.conditions.exists(c, c.type == 'Programmed' && c.status == 'True')) ? true : oldSelf.spec.serviceRef == self.spec.serviceRef", message="spec.serviceRef is immutable when an entity is already Programmed"
// +kubebuilder:validation:XValidation:rule="has(self.spec.protocols) && self.spec.protocols.exists(p, p == 'http') ? (has(self.spec.hosts) || has(self.spec.methods) || has(self.spec.paths) || has(self.spec.paths) || has(self.spec.paths) || has(self.spec.headers) ) : true", message="If protocols has 'http', at least one of 'hosts', 'methods', 'paths' or 'headers' must be set"
// +kubebuilder:validation:XValidation:rule="has(self.spec.controlPlaneRef) && !has(self.spec.serviceRef) || !has(self.spec.controlPlaneRef) && has(self.spec.serviceRef)", message="Only one of controlPlaneRef or serviceRef can be set"
// +kubebuilder:validation:XValidation:rule="!has(self.spec.controlPlaneRef) ? true : !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.serviceRef) ? true : (!self.status.conditions.exists(c, c.type == 'Programmed' && c.status == 'True')) ? true : oldSelf.spec.serviceRef == self.spec.serviceRef", message="spec.serviceRef is immutable when an entity is already Programmed"
// +kubebuilder:validation:XValidation:rule="!has(self.spec.controlPlaneRef) ? true :(!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"
type KongRoute struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand All @@ -50,7 +53,14 @@ type KongRoute struct {

// KongRouteSpec defines specification of a Kong Route.
type KongRouteSpec struct {
// ServiceRef is a reference to a Service this Route is associated with.
// ControlPlaneRef is a reference to a ControlPlane this KongRoute is associated with.
// Route can either specify a ControlPlaneRef and be 'serviceless' route or
// specify a ServiceRef and be associated with a Service.
// +optional
ControlPlaneRef *ControlPlaneRef `json:"controlPlaneRef,omitempty"`
// ServiceRef is a reference to a Service this KongRoute is associated with.
// Route can either specify a ControlPlaneRef and be 'serviceless' route or
// specify a ServiceRef and be associated with a Service.
// +optional
ServiceRef *ServiceRef `json:"serviceRef,omitempty"`

Expand Down
5 changes: 5 additions & 0 deletions api/configuration/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

70 changes: 65 additions & 5 deletions config/crd/bases/configuration.konghq.com_kongroutes.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,53 @@ spec:
spec:
description: KongRouteSpec defines specification of a Kong Route.
properties:
controlPlaneRef:
description: |-
ControlPlaneRef is a reference to a ControlPlane this KongRoute is associated with.
Route can either specify a ControlPlaneRef and be 'serviceless' route or
specify a ServiceRef and be associated with a Service.
properties:
konnectID:
description: |-
KonnectID is the schema for the KonnectID type.
This field is required when the Type is konnectID.
type: string
konnectNamespacedRef:
description: |-
KonnectNamespacedRef is a reference to a Konnect Control Plane entity inside the cluster.
It contains the name of the Konnect Control Plane.
This field is required when the Type is konnectNamespacedRef.
properties:
name:
description: Name is the name of the Konnect Control Plane.
type: string
namespace:
description: |-
Namespace is the namespace where the Konnect Control Plane is in.
Currently only cluster scoped resources (KongVault) are allowed to set `konnectNamespacedRef.namespace`.
type: string
required:
- name
type: object
type:
description: |-
Type can be one of:
- konnectID
- konnectNamespacedRef
enum:
- konnectID
- konnectNamespacedRef
type: string
required:
- type
type: object
x-kubernetes-validations:
- message: when type is konnectNamespacedRef, konnectNamespacedRef
must be set
rule: 'self.type == ''konnectNamespacedRef'' ? has(self.konnectNamespacedRef)
: true'
- message: when type is konnectID, konnectID must be set
rule: 'self.type == ''konnectID'' ? has(self.konnectID) : true'
destinations:
description: A list of IP destinations of incoming connections that
match this Route when using stream routing. Each entry is an object
Expand Down Expand Up @@ -137,8 +184,10 @@ spec:
data with chunked transfer encoding.
type: boolean
serviceRef:
description: ServiceRef is a reference to a Service this Route is
associated with.
description: |-
ServiceRef is a reference to a Service this KongRoute is associated with.
Route can either specify a ControlPlaneRef and be 'serviceless' route or
specify a ServiceRef and be associated with a Service.
properties:
namespacedRef:
description: NamespacedRef is a reference to a KongService.
Expand Down Expand Up @@ -295,15 +344,26 @@ spec:
x-kubernetes-validations:
- message: serviceRef is required once set
rule: '!has(oldSelf.spec.serviceRef) || has(self.spec.serviceRef)'
- message: spec.serviceRef is immutable when an entity is already Programmed
rule: '(!self.status.conditions.exists(c, c.type == ''Programmed'' && c.status
== ''True'')) ? true : oldSelf.spec.serviceRef == self.spec.serviceRef'
- message: If protocols has 'http', at least one of 'hosts', 'methods', 'paths'
or 'headers' must be set
rule: 'has(self.spec.protocols) && self.spec.protocols.exists(p, p == ''http'')
? (has(self.spec.hosts) || has(self.spec.methods) || has(self.spec.paths)
|| has(self.spec.paths) || has(self.spec.paths) || has(self.spec.headers)
) : true'
- message: Only one of controlPlaneRef or serviceRef can be set
rule: has(self.spec.controlPlaneRef) && !has(self.spec.serviceRef) || !has(self.spec.controlPlaneRef)
&& has(self.spec.serviceRef)
- message: spec.controlPlaneRef cannot specify namespace for namespaced resource
rule: '!has(self.spec.controlPlaneRef) ? true : !has(self.spec.controlPlaneRef.konnectNamespacedRef)
? true : !has(self.spec.controlPlaneRef.konnectNamespacedRef.__namespace__)'
- message: spec.serviceRef is immutable when an entity is already Programmed
rule: '!has(self.spec.serviceRef) ? true : (!self.status.conditions.exists(c,
c.type == ''Programmed'' && c.status == ''True'')) ? true : oldSelf.spec.serviceRef
== self.spec.serviceRef'
- message: spec.controlPlaneRef is immutable when an entity is already Programmed
rule: '!has(self.spec.controlPlaneRef) ? true :(!self.status.conditions.exists(c,
c.type == ''Programmed'' && c.status == ''True'')) ? true : oldSelf.spec.controlPlaneRef
== self.spec.controlPlaneRef'
served: true
storage: true
subresources:
Expand Down
4 changes: 3 additions & 1 deletion docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,7 @@ _Appears in:_
- [KongKeySetSpec](#kongkeysetspec)
- [KongKeySpec](#kongkeyspec)
- [KongPluginBindingSpec](#kongpluginbindingspec)
- [KongRouteSpec](#kongroutespec)
- [KongServiceSpec](#kongservicespec)
- [KongUpstreamSpec](#kongupstreamspec)
- [KongVaultSpec](#kongvaultspec)
Expand Down Expand Up @@ -1154,7 +1155,8 @@ KongRouteSpec defines specification of a Kong Route.

| Field | Description |
| --- | --- |
| `serviceRef` _[ServiceRef](#serviceref)_ | ServiceRef is a reference to a Service this Route is associated with. |
| `controlPlaneRef` _[ControlPlaneRef](#controlplaneref)_ | ControlPlaneRef is a reference to a ControlPlane this KongRoute is associated with. Route can either specify a ControlPlaneRef and be 'serviceless' route or specify a ServiceRef and be associated with a Service. |
| `serviceRef` _[ServiceRef](#serviceref)_ | ServiceRef is a reference to a Service this KongRoute is associated with. Route can either specify a ControlPlaneRef and be 'serviceless' route or specify a ServiceRef and be associated with a Service. |
| `destinations` _Destinations array_ | A list of IP destinations of incoming connections that match this Route when using stream routing. Each entry is an object with fields "ip" (optionally in CIDR range notation) and/or "port". |
| `headers` _object (keys:string, values:string)_ | One or more lists of values indexed by header name that will cause this Route to match if present in the request. The `Host` header cannot be used with this attribute: hosts should be specified using the `hosts` attribute. When `headers` contains only one value and that value starts with the special prefix `~*`, the value is interpreted as a regular expression. |
| `hosts` _string array_ | A list of domain names that match this Route. Note that the hosts value is case sensitive. |
Expand Down
1 change: 1 addition & 0 deletions test/crdsvalidation/kongroute/testcases/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ var TestCases = []testCasesGroup{}

func init() {
TestCases = append(TestCases,
cpRef,
serviceRef,
protocols,
)
Expand Down
152 changes: 152 additions & 0 deletions test/crdsvalidation/kongroute/testcases/controlplaneref.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package testcases

import (
"github.com/samber/lo"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

configurationv1alpha1 "github.com/kong/kubernetes-configuration/api/configuration/v1alpha1"
)

var cpRef = testCasesGroup{
Name: "cp ref validation",
TestCases: []testCase{
{
Name: "cannot specify with service ref",
KongRoute: configurationv1alpha1.KongRoute{
ObjectMeta: commonObjectMeta,
Spec: configurationv1alpha1.KongRouteSpec{
ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{
Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef,
KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{
Name: "test-konnect-control-plane",
},
},
ServiceRef: &configurationv1alpha1.ServiceRef{
Type: configurationv1alpha1.ServiceRefNamespacedRef,
NamespacedRef: &configurationv1alpha1.NamespacedServiceRef{
Name: "test-konnect-service",
},
},
KongRouteAPISpec: configurationv1alpha1.KongRouteAPISpec{},
},
},
ExpectedErrorMessage: lo.ToPtr("Only one of controlPlaneRef or serviceRef can be set"),
},
{
Name: "konnectNamespacedRef reference is valid",
KongRoute: configurationv1alpha1.KongRoute{
ObjectMeta: commonObjectMeta,
Spec: configurationv1alpha1.KongRouteSpec{
ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{
Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef,
KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{
Name: "test-konnect-control-plane",
},
},
KongRouteAPISpec: configurationv1alpha1.KongRouteAPISpec{},
},
},
},
{
Name: "not providing konnectNamespacedRef when type is konnectNamespacedRef yields an error",
KongRoute: configurationv1alpha1.KongRoute{
ObjectMeta: commonObjectMeta,
Spec: configurationv1alpha1.KongRouteSpec{
ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{
Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef,
},
KongRouteAPISpec: configurationv1alpha1.KongRouteAPISpec{},
},
},
ExpectedErrorMessage: lo.ToPtr("when type is konnectNamespacedRef, konnectNamespacedRef must be set"),
},
{
Name: "not providing konnectID when type is konnectID yields an error",
KongRoute: configurationv1alpha1.KongRoute{
ObjectMeta: commonObjectMeta,
Spec: configurationv1alpha1.KongRouteSpec{
ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{
Type: configurationv1alpha1.ControlPlaneRefKonnectID,
},
KongRouteAPISpec: configurationv1alpha1.KongRouteAPISpec{},
},
},
ExpectedErrorMessage: lo.ToPtr("when type is konnectID, konnectID must be set"),
},
{
Name: "providing namespace in konnectNamespacedRef yields an error",
KongRoute: configurationv1alpha1.KongRoute{
ObjectMeta: commonObjectMeta,
Spec: configurationv1alpha1.KongRouteSpec{
ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{
Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef,
KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{
Name: "test-konnect-control-plane",
Namespace: "another-namespace",
},
},
KongRouteAPISpec: configurationv1alpha1.KongRouteAPISpec{},
},
},
ExpectedErrorMessage: lo.ToPtr("spec.controlPlaneRef cannot specify namespace for namespaced resource"),
},
{
Name: "konnectNamespacedRef reference name cannot be changed when an entity is Programmed",
KongRoute: configurationv1alpha1.KongRoute{
ObjectMeta: commonObjectMeta,
Spec: configurationv1alpha1.KongRouteSpec{
ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{
Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef,
KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{
Name: "test-konnect-control-plane",
},
},
KongRouteAPISpec: configurationv1alpha1.KongRouteAPISpec{},
},
},
KongRouteStatus: &configurationv1alpha1.KongRouteStatus{
Conditions: []metav1.Condition{
{
Type: "Programmed",
Status: metav1.ConditionTrue,
Reason: "Programmed",
LastTransitionTime: metav1.Now(),
},
},
},
Update: func(ks *configurationv1alpha1.KongRoute) {
ks.Spec.ControlPlaneRef.KonnectNamespacedRef.Name = "new-konnect-control-plane"
},
ExpectedUpdateErrorMessage: lo.ToPtr("spec.controlPlaneRef is immutable when an entity is already Programmed"),
},
{
Name: "konnectNamespacedRef reference type cannot be changed when an entity is Programmed",
KongRoute: configurationv1alpha1.KongRoute{
ObjectMeta: commonObjectMeta,
Spec: configurationv1alpha1.KongRouteSpec{
ControlPlaneRef: &configurationv1alpha1.ControlPlaneRef{
Type: configurationv1alpha1.ControlPlaneRefKonnectNamespacedRef,
KonnectNamespacedRef: &configurationv1alpha1.KonnectNamespacedRef{
Name: "test-konnect-control-plane",
},
},
KongRouteAPISpec: configurationv1alpha1.KongRouteAPISpec{},
},
},
KongRouteStatus: &configurationv1alpha1.KongRouteStatus{
Conditions: []metav1.Condition{
{
Type: "Programmed",
Status: metav1.ConditionTrue,
Reason: "Programmed",
LastTransitionTime: metav1.Now(),
},
},
},
Update: func(ks *configurationv1alpha1.KongRoute) {
ks.Spec.ControlPlaneRef.Type = configurationv1alpha1.ControlPlaneRefKonnectID
},
ExpectedUpdateErrorMessage: lo.ToPtr("spec.controlPlaneRef is immutable when an entity is already Programmed"),
},
},
}

0 comments on commit 68e4bb7

Please sign in to comment.