-
Notifications
You must be signed in to change notification settings - Fork 129
Add CEL validation test for targetRef
in ClientSettingsPolicy
#3623
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
base: main
Are you sure you want to change the base?
Changes from all commits
dbb5b5c
48ec86b
c7129bd
87a4e0e
29f201e
d921ed2
f22269a
7fddffa
dd66a79
d664f35
cd9bada
7d9989d
9274e57
506b01c
f99ab29
ad80b70
e5479fb
1789701
aa4a6b2
8f74214
4486bd5
1adf168
7c6dba4
69f485b
1ad24c4
aa5adc6
29f26ef
f26eea3
efa4a81
d25661a
c413895
9962e03
feca4ed
8d84e7b
ddce60f
d518ada
39a6b38
5079c21
ce0960b
248df53
c5860e2
83139ce
a4e58ee
339e6b8
716d051
d273281
4a6e84e
66b7105
510aac1
c45403a
e284740
06adc0e
cb43b52
9217339
f30f117
e18b9dc
869a091
92caf12
81fd868
94a8dd0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package cel | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
. "github.com/onsi/gomega" | ||
controllerruntime "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" | ||
|
||
ngfAPIv1alpha1 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha1" | ||
) | ||
|
||
func TestClientSettingsPoliciesTargetRefKind(t *testing.T) { | ||
shaun-nx marked this conversation as resolved.
Show resolved
Hide resolved
|
||
t.Parallel() | ||
g := NewWithT(t) | ||
k8sClient, err := getKubernetesClient(t) | ||
g.Expect(err).ToNot(HaveOccurred()) | ||
tests := []struct { | ||
policySpec ngfAPIv1alpha1.ClientSettingsPolicySpec | ||
name string | ||
wantErrors []string | ||
}{ | ||
{ | ||
name: "Validate TargetRef of kind Gateway is allowed", | ||
policySpec: ngfAPIv1alpha1.ClientSettingsPolicySpec{ | ||
TargetRef: gatewayv1alpha2.LocalPolicyTargetReference{ | ||
Kind: gatewayKind, | ||
Group: gatewayGroup, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Validate TargetRef of kind HTTPRoute is allowed", | ||
policySpec: ngfAPIv1alpha1.ClientSettingsPolicySpec{ | ||
TargetRef: gatewayv1alpha2.LocalPolicyTargetReference{ | ||
Kind: httpRouteKind, | ||
Group: gatewayGroup, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Validate TargetRef of kind GRPCRoute is allowed", | ||
policySpec: ngfAPIv1alpha1.ClientSettingsPolicySpec{ | ||
TargetRef: gatewayv1alpha2.LocalPolicyTargetReference{ | ||
Kind: grpcRouteKind, | ||
Group: gatewayGroup, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Validate Invalid TargetRef Kind is not allowed", | ||
wantErrors: []string{expectedTargetRefKindError}, | ||
policySpec: ngfAPIv1alpha1.ClientSettingsPolicySpec{ | ||
TargetRef: gatewayv1alpha2.LocalPolicyTargetReference{ | ||
Kind: invalidKind, | ||
Group: gatewayGroup, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Validate TCPRoute TargetRef Kind is not allowed", | ||
wantErrors: []string{expectedTargetRefKindError}, | ||
policySpec: ngfAPIv1alpha1.ClientSettingsPolicySpec{ | ||
TargetRef: gatewayv1alpha2.LocalPolicyTargetReference{ | ||
Kind: tcpRouteKind, | ||
Group: gatewayGroup, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
t.Parallel() | ||
validateClientSettingsPolicy(t, tt, g, k8sClient) | ||
}) | ||
} | ||
} | ||
|
||
func TestClientSettingsPoliciesTargetRefGroup(t *testing.T) { | ||
t.Parallel() | ||
g := NewWithT(t) | ||
k8sClient, err := getKubernetesClient(t) | ||
g.Expect(err).ToNot(HaveOccurred()) | ||
sjberman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
tests := []struct { | ||
policySpec ngfAPIv1alpha1.ClientSettingsPolicySpec | ||
name string | ||
wantErrors []string | ||
}{ | ||
{ | ||
name: "Validate gateway.networking.k8s.io TargetRef Group is allowed", | ||
policySpec: ngfAPIv1alpha1.ClientSettingsPolicySpec{ | ||
TargetRef: gatewayv1alpha2.LocalPolicyTargetReference{ | ||
Kind: gatewayKind, | ||
Group: gatewayGroup, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Validate invalid.networking.k8s.io TargetRef Group is not allowed", | ||
wantErrors: []string{expectedTargetRefGroupError}, | ||
policySpec: ngfAPIv1alpha1.ClientSettingsPolicySpec{ | ||
TargetRef: gatewayv1alpha2.LocalPolicyTargetReference{ | ||
Kind: gatewayKind, | ||
Group: invalidGroup, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "Validate discovery.k8s.io/v1 TargetRef Group is not allowed", | ||
wantErrors: []string{expectedTargetRefGroupError}, | ||
policySpec: ngfAPIv1alpha1.ClientSettingsPolicySpec{ | ||
TargetRef: gatewayv1alpha2.LocalPolicyTargetReference{ | ||
Kind: gatewayKind, | ||
Group: discoveryGroup, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
t.Parallel() | ||
validateClientSettingsPolicy(t, tt, g, k8sClient) | ||
}) | ||
} | ||
} | ||
|
||
func validateClientSettingsPolicy(t *testing.T, tt struct { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something to just keep in mind for when we write more CEL tests for other policies, is that a lot of this function (and |
||
policySpec ngfAPIv1alpha1.ClientSettingsPolicySpec | ||
name string | ||
wantErrors []string | ||
}, g *WithT, k8sClient client.Client, | ||
) { | ||
t.Helper() | ||
sjberman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
policySpec := tt.policySpec | ||
policySpec.TargetRef.Name = gatewayv1alpha2.ObjectName(uniqueResourceName(testTargetRefName)) | ||
policyName := uniqueResourceName(testPolicyName) | ||
|
||
clientSettingsPolicy := &ngfAPIv1alpha1.ClientSettingsPolicy{ | ||
ObjectMeta: controllerruntime.ObjectMeta{ | ||
Name: policyName, | ||
Namespace: defaultNamespace, | ||
}, | ||
Spec: policySpec, | ||
} | ||
|
||
err := k8sClient.Create(context.Background(), clientSettingsPolicy) | ||
sarthyparty marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the k8sClient is using the actual Kubernetes API Server, I wonder if we need to add a timeout
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It probably is fine without it but just curious There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, when creating we typically do have a context to make sure it doesn't block there. Like these constants https://github.com/nginx/nginx-gateway-fabric/blob/main/tests/framework/timeout.go |
||
|
||
// Clean up after test | ||
defer func() { | ||
_ = k8sClient.Delete(context.Background(), clientSettingsPolicy) | ||
}() | ||
|
||
if len(tt.wantErrors) == 0 { | ||
g.Expect(err).ToNot(HaveOccurred()) | ||
} else { | ||
g.Expect(err).To(HaveOccurred()) | ||
for _, wantError := range tt.wantErrors { | ||
g.Expect(err.Error()).To(ContainSubstring(wantError), "Expected error '%s' not found in: %s", wantError, err.Error()) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package cel | ||
|
||
import ( | ||
"crypto/rand" | ||
"fmt" | ||
"testing" | ||
|
||
"k8s.io/apimachinery/pkg/runtime" | ||
controllerruntime "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
ngfAPIv1alpha1 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha1" | ||
ngfAPIv1alpha2 "github.com/nginx/nginx-gateway-fabric/v2/apis/v1alpha2" | ||
) | ||
|
||
const ( | ||
gatewayKind = "Gateway" | ||
httpRouteKind = "HTTPRoute" | ||
grpcRouteKind = "GRPCRoute" | ||
tcpRouteKind = "TCPRoute" | ||
invalidKind = "InvalidKind" | ||
) | ||
|
||
const ( | ||
gatewayGroup = "gateway.networking.k8s.io" | ||
invalidGroup = "invalid.networking.k8s.io" | ||
discoveryGroup = "discovery.k8s.io/v1" | ||
) | ||
|
||
const ( | ||
expectedTargetRefKindError = `TargetRef Kind must be one of: Gateway, HTTPRoute, or GRPCRoute` | ||
expectedTargetRefGroupError = `TargetRef Group must be gateway.networking.k8s.io.` | ||
) | ||
|
||
const ( | ||
defaultNamespace = "default" | ||
) | ||
|
||
const ( | ||
testPolicyName = "test-policy" | ||
testTargetRefName = "test-targetRef" | ||
) | ||
|
||
// getKubernetesClient returns a client connected to a real Kubernetes cluster. | ||
func getKubernetesClient(t *testing.T) (k8sClient client.Client, err error) { | ||
t.Helper() | ||
// Use controller-runtime to get cluster connection | ||
k8sConfig, err := controllerruntime.GetConfig() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Set up scheme with NGF types | ||
scheme := runtime.NewScheme() | ||
if err = ngfAPIv1alpha1.AddToScheme(scheme); err != nil { | ||
return nil, err | ||
} | ||
if err = ngfAPIv1alpha2.AddToScheme(scheme); err != nil { | ||
return nil, err | ||
} | ||
// Create a new client with the scheme and return it | ||
return client.New(k8sConfig, client.Options{Scheme: scheme}) | ||
} | ||
|
||
// randomPrimeNumber generates a random prime number of 64 bits. | ||
// It panics if it fails to generate a random prime number. | ||
func randomPrimeNumber() int64 { | ||
primeNum, err := rand.Prime(rand.Reader, 64) | ||
if err != nil { | ||
panic(fmt.Errorf("failed to generate random prime number: %w", err)) | ||
} | ||
return primeNum.Int64() | ||
} | ||
|
||
// uniqueResourceName generates a unique resource name by appending a random prime number to the given name. | ||
func uniqueResourceName(name string) string { | ||
return fmt.Sprintf("%s-%d", name, randomPrimeNumber()) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package cel | ||
|
||
import ( | ||
"testing" | ||
|
||
. "github.com/onsi/gomega" | ||
) | ||
|
||
func TestMustGenerateRandomPrimeNumber(t *testing.T) { | ||
t.Parallel() | ||
g := NewWithT(t) | ||
g.Expect(func() { | ||
_ = randomPrimeNumber() | ||
}).ToNot(Panic()) | ||
} | ||
|
||
func TestMustReturnUniqueResourceName(t *testing.T) { | ||
t.Parallel() | ||
g := NewWithT(t) | ||
|
||
name := "test-resource" | ||
uniqueName := uniqueResourceName(name) | ||
|
||
g.Expect(uniqueName).To(HavePrefix(name)) | ||
g.Expect(len(uniqueName)).To(BeNumerically(">", len(name))) | ||
} |
Uh oh!
There was an error while loading. Please reload this page.