Skip to content

Commit

Permalink
feat: add KongUpstreamPolicy CRD validation rules (#4955)
Browse files Browse the repository at this point in the history
  • Loading branch information
czeslavo committed Oct 24, 2023
1 parent 80bc869 commit 47b4d24
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ spec:
description: HTTPStatuses is a list of HTTP status codes
that Kong considers a success.
items:
description: HTTPStatus is an HTTP status code.
maximum: 599
minimum: 100
type: integer
type: array
interval:
Expand Down Expand Up @@ -197,6 +200,9 @@ spec:
description: HTTPStatuses is a list of HTTP status codes
that Kong considers a failure.
items:
description: HTTPStatus is an HTTP status code.
maximum: 599
minimum: 100
type: integer
type: array
interval:
Expand Down Expand Up @@ -228,6 +234,9 @@ spec:
description: HTTPStatuses is a list of HTTP status codes
that Kong considers a success.
items:
description: HTTPStatus is an HTTP status code.
maximum: 599
minimum: 100
type: integer
type: array
interval:
Expand Down Expand Up @@ -267,6 +276,9 @@ spec:
description: HTTPStatuses is a list of HTTP status codes
that Kong considers a failure.
items:
description: HTTPStatus is an HTTP status code.
maximum: 599
minimum: 100
type: integer
type: array
interval:
Expand Down Expand Up @@ -305,6 +317,29 @@ spec:
type: integer
type: object
type: object
x-kubernetes-validations:
- message: Only one of spec.hashOn.(cookie|header|uriCapture|queryArg) can
be set.
rule: 'has(self.spec.hashOn) ? [has(self.spec.hashOn.cookie), has(self.spec.hashOn.header),
has(self.spec.hashOn.uriCapture), has(self.spec.hashOn.queryArg)].filter(fieldSet,
fieldSet == true).size() <= 1 : true'
- message: Only one of spec.hashOnFallback.(header|uriCapture|queryArg) can
be set.
rule: 'has(self.spec.hashOnFallback) ? [has(self.spec.hashOnFallback.header),
has(self.spec.hashOnFallback.uriCapture), has(self.spec.hashOnFallback.queryArg)].filter(fieldSet,
fieldSet == true).size() <= 1 : true'
- message: When spec.hashOn.cookie is set, spec.hashOn.cookiePath is required.
rule: 'has(self.spec.hashOn) && has(self.spec.hashOn.cookie) ? has(self.spec.hashOn.cookiePath)
: true'
- message: When spec.hashOn.cookiePath is set, spec.hashOn.cookie is required.
rule: 'has(self.spec.hashOn) && has(self.spec.hashOn.cookiePath) ? has(self.spec.hashOn.cookie)
: true'
- message: spec.hashOnFallback.cookie must not be set.
rule: 'has(self.spec.hashOnFallback) ? !has(self.spec.hashOnFallback.cookie)
: true'
- message: spec.hashOnFallback.cookiePath must not be set.
rule: 'has(self.spec.hashOnFallback) ? !has(self.spec.hashOnFallback.cookiePath)
: true'
served: true
storage: true
subresources:
Expand Down
18 changes: 16 additions & 2 deletions docs/api-reference.md

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

29 changes: 22 additions & 7 deletions internal/dataplane/parser/translators/kongupstreampolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import (
kongv1beta1 "github.com/kong/kubernetes-ingress-controller/v2/pkg/apis/configuration/v1beta1"
)

const (
KongHashOnTypeHeader string = "header"
KongHashOnTypeCookie string = "cookie"
KongHashOnTypeQueryArg string = "query_arg"
KongHashOnTypeURICapture string = "uri_capture"
)

// TranslateKongUpstreamPolicy translates KongUpstreamPolicySpec to kong.Upstream. It makes assumption that
// KongUpstreamPolicySpec has been validated on the API level.
func TranslateKongUpstreamPolicy(policy kongv1beta1.KongUpstreamPolicySpec) *kong.Upstream {
Expand Down Expand Up @@ -35,16 +42,15 @@ func translateHashOn(hashOn *kongv1beta1.KongUpstreamHash) *string {
return nil
}
// CRD validations will ensure only one of hashOn fields can be set, therefore the order doesn't matter.
// TODO: https://github.com/Kong/kubernetes-ingress-controller/issues/4951
switch {
case hashOn.Header != nil:
return lo.ToPtr("header")
return lo.ToPtr(KongHashOnTypeHeader)
case hashOn.Cookie != nil:
return lo.ToPtr("cookie")
return lo.ToPtr(KongHashOnTypeCookie)
case hashOn.QueryArg != nil:
return lo.ToPtr("query_arg")
return lo.ToPtr(KongHashOnTypeQueryArg)
case hashOn.URICapture != nil:
return lo.ToPtr("uri_capture")
return lo.ToPtr(KongHashOnTypeURICapture)
default:
return nil
}
Expand Down Expand Up @@ -128,7 +134,7 @@ func translateHealthy(healthy *kongv1beta1.KongUpstreamHealthcheckHealthy) *kong
return nil
}
return &kong.Healthy{
HTTPStatuses: healthy.HTTPStatuses,
HTTPStatuses: translateHTTPStatuses(healthy.HTTPStatuses),
Interval: healthy.Interval,
Successes: healthy.Successes,
}
Expand All @@ -140,9 +146,18 @@ func translateUnhealthy(unhealthy *kongv1beta1.KongUpstreamHealthcheckUnhealthy)
}
return &kong.Unhealthy{
HTTPFailures: unhealthy.HTTPFailures,
HTTPStatuses: unhealthy.HTTPStatuses,
HTTPStatuses: translateHTTPStatuses(unhealthy.HTTPStatuses),
TCPFailures: unhealthy.TCPFailures,
Timeouts: unhealthy.Timeouts,
Interval: unhealthy.Interval,
}
}

func translateHTTPStatuses(statuses []kongv1beta1.HTTPStatus) []int {
if statuses == nil {
return nil
}
// Using lo.Map only in case healthy.HTTPStatuses is not nil, because lo.Map creates a non-nil slice even
// if the input slice is nil.
return lo.Map(statuses, func(s kongv1beta1.HTTPStatus, _ int) int { return int(s) })
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,13 @@ func TestTranslateKongUpstreamPolicy(t *testing.T) {
Type: lo.ToPtr("http"),
Concurrency: lo.ToPtr(10),
Healthy: &kongv1beta1.KongUpstreamHealthcheckHealthy{
HTTPStatuses: []int{200},
HTTPStatuses: []kongv1beta1.HTTPStatus{200},
Interval: lo.ToPtr(20),
Successes: lo.ToPtr(30),
},
Unhealthy: &kongv1beta1.KongUpstreamHealthcheckUnhealthy{
HTTPFailures: lo.ToPtr(40),
HTTPStatuses: []int{500},
HTTPStatuses: []kongv1beta1.HTTPStatus{500},
Timeouts: lo.ToPtr(60),
Interval: lo.ToPtr(70),
},
Expand Down
15 changes: 13 additions & 2 deletions pkg/apis/configuration/v1beta1/kongupstreampolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ func init() {
// +kubebuilder:subresource:status
// +kubebuilder:storageversion
// +kubebuilder:metadata:labels=gateway.networking.k8s.io/policy=direct
// +kubebuilder:validation:XValidation:rule="has(self.spec.hashOn) ? [has(self.spec.hashOn.cookie), has(self.spec.hashOn.header), has(self.spec.hashOn.uriCapture), has(self.spec.hashOn.queryArg)].filter(fieldSet, fieldSet == true).size() <= 1 : true", message="Only one of spec.hashOn.(cookie|header|uriCapture|queryArg) can be set."
// +kubebuilder:validation:XValidation:rule="has(self.spec.hashOnFallback) ? [has(self.spec.hashOnFallback.header), has(self.spec.hashOnFallback.uriCapture), has(self.spec.hashOnFallback.queryArg)].filter(fieldSet, fieldSet == true).size() <= 1 : true", message="Only one of spec.hashOnFallback.(header|uriCapture|queryArg) can be set."
// +kubebuilder:validation:XValidation:rule="has(self.spec.hashOn) && has(self.spec.hashOn.cookie) ? has(self.spec.hashOn.cookiePath) : true", message="When spec.hashOn.cookie is set, spec.hashOn.cookiePath is required."
// +kubebuilder:validation:XValidation:rule="has(self.spec.hashOn) && has(self.spec.hashOn.cookiePath) ? has(self.spec.hashOn.cookie) : true", message="When spec.hashOn.cookiePath is set, spec.hashOn.cookie is required."
// +kubebuilder:validation:XValidation:rule="has(self.spec.hashOnFallback) ? !has(self.spec.hashOnFallback.cookie) : true", message="spec.hashOnFallback.cookie must not be set."
// +kubebuilder:validation:XValidation:rule="has(self.spec.hashOnFallback) ? !has(self.spec.hashOnFallback.cookiePath) : true", message="spec.hashOnFallback.cookiePath must not be set."
type KongUpstreamPolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand Down Expand Up @@ -165,10 +171,15 @@ type KongUpstreamPassiveHealthcheck struct {
Unhealthy *KongUpstreamHealthcheckUnhealthy `json:"unhealthy,omitempty"`
}

// HTTPStatus is an HTTP status code.
// +kubebuilder:validation:Minimum=100
// +kubebuilder:validation:Maximum=599
type HTTPStatus int

// KongUpstreamHealthcheckHealthy configures thresholds and HTTP status codes to mark targets healthy for an upstream.
type KongUpstreamHealthcheckHealthy struct {
// HTTPStatuses is a list of HTTP status codes that Kong considers a success.
HTTPStatuses []int `json:"httpStatuses,omitempty"`
HTTPStatuses []HTTPStatus `json:"httpStatuses,omitempty"`

// Interval is the interval between active health checks for an upstream in seconds when in a healthy state.
// +kubebuilder:validation:Minimum=0
Expand All @@ -186,7 +197,7 @@ type KongUpstreamHealthcheckUnhealthy struct {
HTTPFailures *int `json:"httpFailures,omitempty"`

// HTTPStatuses is a list of HTTP status codes that Kong considers a failure.
HTTPStatuses []int `json:"httpStatuses,omitempty"`
HTTPStatuses []HTTPStatus `json:"httpStatuses,omitempty"`

// TCPFailures is the number of TCP failures in a row to consider a target unhealthy.
// +kubebuilder:validation:Minimum=0
Expand Down
4 changes: 2 additions & 2 deletions pkg/apis/configuration/v1beta1/zz_generated.deepcopy.go

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

Loading

0 comments on commit 47b4d24

Please sign in to comment.