Skip to content

Add CEL validation tests for ObservabilityPolicy CRD #3735

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion tests/cel/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const (
// ClientSettingsPolicy validation errors.
const (
expectedTargetRefKindError = `TargetRef Kind must be one of: Gateway, HTTPRoute, or GRPCRoute`
expectedTargetRefGroupError = `TargetRef Group must be gateway.networking.k8s.io.`
expectedTargetRefGroupError = `TargetRef Group must be gateway.networking.k8s.io`
expectedHeaderWithoutServerError = `header can only be specified if server is specified`
)

Expand All @@ -44,6 +44,13 @@ const (
expectedMinReplicasLessThanOrEqualError = `minReplicas must be less than or equal to maxReplicas`
)

// ObservabilityPolicy validation errors.
const (
expectedTargetRefMustBeHTTPRouteOrGrpcRouteError = `TargetRef Kind must be: HTTPRoute or GRPCRoute`
expectedTargetRefKindAndNameComboMustBeUnique = `TargetRef Kind and Name combination must be unique`
expectedStrategyMustBeOfTypeRatio = `ratio can only be specified if strategy is of type ratio`
)

const (
defaultNamespace = "default"
)
Expand Down
326 changes: 326 additions & 0 deletions tests/cel/observabilitypolicy_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,326 @@
package cel

import (
"testing"

controllerruntime "sigs.k8s.io/controller-runtime"
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"

ngfAPIv1alpha2 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha2"
"github.com/nginx/nginx-gateway-fabric/v2/internal/framework/helpers"
)

func TestObservabilityPoliciesTargetRefKind(t *testing.T) {
t.Parallel()
k8sClient := getKubernetesClient(t)

tests := []struct {
spec ngfAPIv1alpha2.ObservabilityPolicySpec
name string
wantErrors []string
}{
{
name: "Validate TargetRef of kind HTTPRoute is allowed",
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: httpRouteKind,
Group: gatewayGroup,
},
},
},
},
{
name: "Validate TargetRef of kind GRPCRoute is allowed",
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: grpcRouteKind,
Group: gatewayGroup,
},
},
},
},
{
name: "Validate Invalid TargetRef Kind is not allowed",
wantErrors: []string{expectedTargetRefMustBeHTTPRouteOrGrpcRouteError},
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: invalidKind,
Group: gatewayGroup,
},
},
},
},
{
name: "Validate TCPRoute TargetRef Kind is not allowed",
wantErrors: []string{expectedTargetRefMustBeHTTPRouteOrGrpcRouteError},
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: tcpRouteKind,
Group: gatewayGroup,
},
},
},
},
{
name: "Validate TargetRef of kind Gateway is not allowed",
wantErrors: []string{expectedTargetRefMustBeHTTPRouteOrGrpcRouteError},
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: gatewayKind,
Group: gatewayGroup,
},
},
},
},
{
name: "Validate ObservabilityPolicy is applied when one TargetRef is valid and another is invalid",
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: gatewayKind,
Group: gatewayGroup,
},
{
Kind: grpcRouteKind,
Group: gatewayGroup,
},
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
spec := tt.spec

for i := range spec.TargetRefs {
spec.TargetRefs[i].Name = gatewayv1alpha2.ObjectName(uniqueResourceName(testTargetRefName))
}

observabilityPolicy := &ngfAPIv1alpha2.ObservabilityPolicy{
ObjectMeta: controllerruntime.ObjectMeta{
Name: uniqueResourceName(testResourceName),
Namespace: defaultNamespace,
},
Spec: spec,
}
validateCrd(t, tt.wantErrors, observabilityPolicy, k8sClient)
})
}
}

func TestObservabilityPoliciesTargetRefGroup(t *testing.T) {
t.Parallel()
k8sClient := getKubernetesClient(t)

tests := []struct {
spec ngfAPIv1alpha2.ObservabilityPolicySpec
name string
wantErrors []string
}{
{
name: "Validate gateway.networking.k8s.io TargetRef Group is allowed",
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: httpRouteKind,
Group: gatewayGroup,
},
},
},
},
{
name: "Validate invalid.networking.k8s.io TargetRef Group is not allowed",
wantErrors: []string{expectedTargetRefGroupError},
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: httpRouteKind,
Group: invalidGroup,
},
},
},
},
{
name: "Validate discovery.k8s.io/v1 TargetRef Group is not allowed",
wantErrors: []string{expectedTargetRefGroupError},
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: httpRouteKind,
Group: discoveryGroup,
},
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
spec := tt.spec

for i := range spec.TargetRefs {
spec.TargetRefs[i].Name = gatewayv1alpha2.ObjectName(uniqueResourceName(testTargetRefName))
}

observabilityPolicy := &ngfAPIv1alpha2.ObservabilityPolicy{
ObjectMeta: controllerruntime.ObjectMeta{
Name: uniqueResourceName(testResourceName),
Namespace: defaultNamespace,
},
Spec: spec,
}
validateCrd(t, tt.wantErrors, observabilityPolicy, k8sClient)
})
}
}

func TestObservabilityPoliciesTargetRefKindAndNameCombo(t *testing.T) {
t.Parallel()
k8sClient := getKubernetesClient(t)

tests := []struct {
spec ngfAPIv1alpha2.ObservabilityPolicySpec
name string
wantErrors []string
}{
{
name: "Validate resource is invalid when TargetRef Kind and Name combination is not unique",
wantErrors: []string{expectedTargetRefKindAndNameComboMustBeUnique},
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: httpRouteKind,
Name: gatewayv1alpha2.ObjectName(testTargetRefName),
Group: gatewayGroup,
},
{
Kind: httpRouteKind,
Name: gatewayv1alpha2.ObjectName(testTargetRefName),
Group: gatewayGroup,
},
},
},
},
{
name: "Validate resource is valid when TargetRef Kind and Name combination is unique using different kinds",
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: httpRouteKind,
Name: gatewayv1alpha2.ObjectName(testTargetRefName),
Group: gatewayGroup,
},
{
Kind: grpcRouteKind,
Name: gatewayv1alpha2.ObjectName(testTargetRefName),
Group: gatewayGroup,
},
},
},
},
{
name: "Validate resource is valid when TargetRef Kind and Name combination is unique using different names",
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: httpRouteKind,
Name: gatewayv1alpha2.ObjectName(uniqueResourceName(testTargetRefName)),
Group: gatewayGroup,
},
{
Kind: grpcRouteKind,
Name: gatewayv1alpha2.ObjectName(uniqueResourceName(testTargetRefName)),
Group: gatewayGroup,
},
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
spec := tt.spec

observabilityPolicy := &ngfAPIv1alpha2.ObservabilityPolicy{
ObjectMeta: controllerruntime.ObjectMeta{
Name: uniqueResourceName(testResourceName),
Namespace: defaultNamespace,
},
Spec: spec,
}
validateCrd(t, tt.wantErrors, observabilityPolicy, k8sClient)
})
}
}

func TestObservabilityPoliciesTracing(t *testing.T) {
t.Parallel()
k8sClient := getKubernetesClient(t)

tests := []struct {
spec ngfAPIv1alpha2.ObservabilityPolicySpec
name string
wantErrors []string
}{
{
name: "Validate ObservabilityPolicy is applied when ratio is set and strategy is TraceStrategyRatio",
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: httpRouteKind,
Group: gatewayGroup,
},
},
Tracing: &ngfAPIv1alpha2.Tracing{
Strategy: ngfAPIv1alpha2.TraceStrategyRatio,
Ratio: helpers.GetPointer[int32](50),
},
},
},
{
name: "Validate ObservabilityPolicy is invalid when ratio is set and strategy is not TraceStrategyRatio",
wantErrors: []string{expectedStrategyMustBeOfTypeRatio},
spec: ngfAPIv1alpha2.ObservabilityPolicySpec{
TargetRefs: []gatewayv1alpha2.LocalPolicyTargetReference{
{
Kind: httpRouteKind,
Group: gatewayGroup,
},
},
Tracing: &ngfAPIv1alpha2.Tracing{
Strategy: ngfAPIv1alpha2.TraceStrategyParent,
Ratio: helpers.GetPointer[int32](50),
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
spec := tt.spec

for i := range spec.TargetRefs {
spec.TargetRefs[i].Name = gatewayv1alpha2.ObjectName(uniqueResourceName(testTargetRefName))
}

observabilityPolicy := &ngfAPIv1alpha2.ObservabilityPolicy{
ObjectMeta: controllerruntime.ObjectMeta{
Name: uniqueResourceName(testResourceName),
Namespace: defaultNamespace,
},
Spec: spec,
}
validateCrd(t, tt.wantErrors, observabilityPolicy, k8sClient)
})
}
}
Loading