Skip to content
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

CEL validation: implement validateProvider for EnvoyProxy #2081

Merged
merged 6 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions api/v1alpha1/envoyproxy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ type EnvoyProxyKubernetesProvider struct {
// are applied.
//
// +optional
// +kubebuilder:validation:XValidation:message="allocateLoadBalancerNodePorts can only be set for LoadBalancer type",rule="!has(self.allocateLoadBalancerNodePorts) || self.type == 'LoadBalancer'"
// +kubebuilder:validation:XValidation:message="loadBalancerIP can only be set for LoadBalancer type",rule="!has(self.loadBalancerIP) || self.type == 'LoadBalancer'"
// +kubebuilder:validation:XValidation:message="loadBalancerIP must be a valid IPv4 address",rule="!has(self.loadBalancerIP) || self.loadBalancerIP.matches(r\"^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4})\")"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this's hard to read, but I cannot find better solution right now.
Hope one day, there's ip lib from kubernetest just like url.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, this is just kind of the nature of CEL for more complex logic.

EnvoyService *KubernetesServiceSpec `json:"envoyService,omitempty"`
}

Expand Down
2 changes: 2 additions & 0 deletions api/v1alpha1/validation/envoyproxy_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ func validateEnvoyProxySpec(spec *egv1a1.EnvoyProxySpec) error {
return utilerrors.NewAggregate(errs)
}

// TODO: remove this function if CEL validation became stable
func validateProvider(spec *egv1a1.EnvoyProxySpec) []error {
var errs []error
if spec != nil && spec.Provider != nil {
Expand All @@ -80,6 +81,7 @@ func validateProvider(spec *egv1a1.EnvoyProxySpec) []error {
return errs
}

// TODO: remove this function if CEL validation became stable
func validateService(spec *egv1a1.EnvoyProxySpec) []error {
var errs []error
if spec.Provider.Kubernetes != nil && spec.Provider.Kubernetes.EnvoyService != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5280,6 +5280,16 @@ spec:
- NodePort
type: string
type: object
x-kubernetes-validations:
- message: allocateLoadBalancerNodePorts can only be set for
LoadBalancer type
rule: '!has(self.allocateLoadBalancerNodePorts) || self.type
== ''LoadBalancer'''
- message: loadBalancerIP can only be set for LoadBalancer
type
rule: '!has(self.loadBalancerIP) || self.type == ''LoadBalancer'''
- message: loadBalancerIP must be a valid IPv4 address
rule: '!has(self.loadBalancerIP) || self.loadBalancerIP.matches(r"^(((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4})")'
type: object
type:
description: Type is the type of resource provider to use. A resource
Expand Down
60 changes: 37 additions & 23 deletions internal/provider/kubernetes/kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func startEnv() (*envtest.Environment, *rest.Config, error) {
return env, cfg, nil
}

func testGatewayClassController(ctx context.Context, t *testing.T, provider *Provider, resources *message.ProviderResources) {
func testGatewayClassController(ctx context.Context, t *testing.T, provider *Provider, _ *message.ProviderResources) {
cli := provider.manager.GetClient()

gc := test.GetGatewayClass("test-gc-controllername", egv1a1.GatewayControllerName)
Expand Down Expand Up @@ -171,7 +171,7 @@ func testGatewayClassWithParamRef(ctx context.Context, t *testing.T, provider *P
gc := test.GetGatewayClass("gc-with-param-ref", egv1a1.GatewayControllerName)
gc.Spec.ParametersRef = &gwapiv1.ParametersReference{
Group: gwapiv1.Group(egv1a1.GroupVersion.Group),
Kind: gwapiv1.Kind(egv1a1.KindEnvoyProxy),
Kind: egv1a1.KindEnvoyProxy,
Name: epName,
Namespace: gatewayapi.NamespacePtr(testNs),
}
Expand Down Expand Up @@ -562,6 +562,7 @@ func testAuthenFilter(ctx context.Context, t *testing.T, provider *Provider, res
BackendRef: gwapiv1.BackendRef{
BackendObjectReference: gwapiv1.BackendObjectReference{
Name: "test",
Port: ptr.To(gwapiv1.PortNumber(80)),
},
},
},
Expand Down Expand Up @@ -744,6 +745,7 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour
BackendRef: gwapiv1.BackendRef{
BackendObjectReference: gwapiv1.BackendObjectReference{
Name: "test",
Port: ptr.To(gwapiv1.PortNumber(80)),
},
},
},
Expand Down Expand Up @@ -779,15 +781,6 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour
},
},
},
BackendRefs: []gwapiv1.HTTPBackendRef{
{
BackendRef: gwapiv1.BackendRef{
BackendObjectReference: gwapiv1.BackendObjectReference{
Name: "test",
},
},
},
},
Filters: []gwapiv1.HTTPRouteFilter{
{
Type: gwapiv1.HTTPRouteFilterType("RequestRedirect"),
Expand Down Expand Up @@ -839,6 +832,7 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour
BackendRef: gwapiv1.BackendRef{
BackendObjectReference: gwapiv1.BackendObjectReference{
Name: "test",
Port: ptr.To(gwapiv1.PortNumber(80)),
},
},
},
Expand Down Expand Up @@ -891,6 +885,7 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour
BackendRef: gwapiv1.BackendRef{
BackendObjectReference: gwapiv1.BackendObjectReference{
Name: "test",
Port: ptr.To(gwapiv1.PortNumber(80)),
},
},
},
Expand Down Expand Up @@ -954,6 +949,7 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour
BackendRef: gwapiv1.BackendRef{
BackendObjectReference: gwapiv1.BackendObjectReference{
Name: "test",
Port: ptr.To(gwapiv1.PortNumber(80)),
},
},
},
Expand Down Expand Up @@ -1006,6 +1002,7 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour
BackendRef: gwapiv1.BackendRef{
BackendObjectReference: gwapiv1.BackendObjectReference{
Name: "test",
Port: ptr.To(gwapiv1.PortNumber(80)),
},
},
},
Expand Down Expand Up @@ -1069,6 +1066,7 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour
BackendRef: gwapiv1.BackendRef{
BackendObjectReference: gwapiv1.BackendObjectReference{
Name: "test",
Port: ptr.To(gwapiv1.PortNumber(80)),
},
},
},
Expand Down Expand Up @@ -1121,6 +1119,7 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour
BackendRef: gwapiv1.BackendRef{
BackendObjectReference: gwapiv1.BackendObjectReference{
Name: "test",
Port: ptr.To(gwapiv1.PortNumber(80)),
},
},
},
Expand All @@ -1131,6 +1130,7 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour
RequestMirror: &gwapiv1.HTTPRequestMirrorFilter{
BackendRef: gwapiv1.BackendObjectReference{
Name: "test",
Port: ptr.To(gwapiv1.PortNumber(80)),
},
},
},
Expand Down Expand Up @@ -1185,6 +1185,11 @@ func testHTTPRoute(ctx context.Context, t *testing.T, provider *Provider, resour

// Ensure the Service is in the resource map.
require.Eventually(t, func() bool {
// The redirect test case does not have a service.
if testCase.name == "redirect-httproute" {
return true
}

res, ok := resources.GatewayAPIResources.Load("httproute-test")
if !ok {
return false
Expand Down Expand Up @@ -1226,6 +1231,9 @@ func testTLSRoute(ctx context.Context, t *testing.T, provider *Provider, resourc
Name: "test",
Port: gwapiv1.PortNumber(int32(8080)),
Protocol: gwapiv1.TLSProtocolType,
TLS: &gwapiv1.GatewayTLSConfig{
Mode: ptr.To(gwapiv1.TLSModePassthrough),
},
},
},
},
Expand Down Expand Up @@ -1270,6 +1278,7 @@ func testTLSRoute(ctx context.Context, t *testing.T, provider *Provider, resourc
{
BackendObjectReference: gwapiv1a2.BackendObjectReference{
Name: "test",
Port: ptr.To(gwapiv1.PortNumber(90)),
},
},
},
Expand Down Expand Up @@ -1371,6 +1380,9 @@ func testServiceCleanupForMultipleRoutes(ctx context.Context, t *testing.T, prov
Name: "tlstest",
Port: gwapiv1.PortNumber(int32(8043)),
Protocol: gwapiv1.TLSProtocolType,
TLS: &gwapiv1.GatewayTLSConfig{
Mode: ptr.To(gwapiv1.TLSModePassthrough),
},
},
},
},
Expand Down Expand Up @@ -1405,6 +1417,7 @@ func testServiceCleanupForMultipleRoutes(ctx context.Context, t *testing.T, prov
BackendRefs: []gwapiv1a2.BackendRef{{
BackendObjectReference: gwapiv1a2.BackendObjectReference{
Name: "test-common-svc",
Port: ptr.To(gwapiv1.PortNumber(90)),
}},
}},
},
Expand Down Expand Up @@ -1434,6 +1447,7 @@ func testServiceCleanupForMultipleRoutes(ctx context.Context, t *testing.T, prov
BackendRef: gwapiv1.BackendRef{
BackendObjectReference: gwapiv1.BackendObjectReference{
Name: "test-common-svc",
Port: ptr.To(gwapiv1.PortNumber(80)),
},
},
}},
Expand Down Expand Up @@ -1669,13 +1683,13 @@ func TestNamespaceSelectorsProvider(t *testing.T) {
Name: "watched-http-route",
},
watchedGateway.Name,
types.NamespacedName{Name: watchedSvc.Name})
types.NamespacedName{Name: watchedSvc.Name}, 80)
watchedHTTPRoute.Spec.Rules[0].Filters = []gwapiv1.HTTPRouteFilter{
{
Type: gwapiv1.HTTPRouteFilterExtensionRef,
ExtensionRef: &gwapiv1.LocalObjectReference{
Group: gwapiv1.Group(egv1a1.GroupVersion.Group),
Kind: gwapiv1.Kind(egv1a1.KindAuthenticationFilter),
Kind: egv1a1.KindAuthenticationFilter,
Name: gwapiv1.ObjectName(watchedAuthenFilter.Name),
},
},
Expand All @@ -1691,13 +1705,13 @@ func TestNamespaceSelectorsProvider(t *testing.T) {
Name: "non-watched-http-route",
},
nonWatchedGateway.Name,
types.NamespacedName{Name: nonWatchedSvc.Name})
types.NamespacedName{Name: nonWatchedSvc.Name}, 8001)
nonWatchedHTTPRoute.Spec.Rules[0].Filters = []gwapiv1.HTTPRouteFilter{
{
Type: gwapiv1.HTTPRouteFilterExtensionRef,
ExtensionRef: &gwapiv1.LocalObjectReference{
Group: gwapiv1.Group(egv1a1.GroupVersion.Group),
Kind: gwapiv1.Kind(egv1a1.KindAuthenticationFilter),
Kind: egv1a1.KindAuthenticationFilter,
Name: gwapiv1.ObjectName(nonWatchedAuthenFilter.Name),
},
},
Expand All @@ -1713,7 +1727,7 @@ func TestNamespaceSelectorsProvider(t *testing.T) {
Name: "watched-grpc-route",
},
watchedGateway.Name,
types.NamespacedName{Name: watchedSvc.Name})
types.NamespacedName{Name: watchedSvc.Name}, 80)
require.NoError(t, cli.Create(ctx, watchedGRPCRoute))
defer func() {
require.NoError(t, cli.Delete(ctx, watchedGRPCRoute))
Expand All @@ -1725,7 +1739,7 @@ func TestNamespaceSelectorsProvider(t *testing.T) {
Name: "non-watched-grpc-route",
},
nonWatchedGateway.Name,
types.NamespacedName{Name: nonWatchedNS.Name})
types.NamespacedName{Name: nonWatchedNS.Name}, 8001)
require.NoError(t, cli.Create(ctx, nonWatchedGRPCRoute))
defer func() {
require.NoError(t, cli.Delete(ctx, nonWatchedGRPCRoute))
Expand All @@ -1737,7 +1751,7 @@ func TestNamespaceSelectorsProvider(t *testing.T) {
Name: "watched-tcp-route",
},
watchedGateway.Name,
types.NamespacedName{Name: watchedSvc.Name})
types.NamespacedName{Name: watchedSvc.Name}, 80)
require.NoError(t, cli.Create(ctx, watchedTCPRoute))
defer func() {
require.NoError(t, cli.Delete(ctx, watchedTCPRoute))
Expand All @@ -1749,7 +1763,7 @@ func TestNamespaceSelectorsProvider(t *testing.T) {
Name: "non-watched-tcp-route",
},
nonWatchedGateway.Name,
types.NamespacedName{Name: nonWatchedNS.Name})
types.NamespacedName{Name: nonWatchedNS.Name}, 80)
require.NoError(t, cli.Create(ctx, nonWatchedTCPRoute))
defer func() {
require.NoError(t, cli.Delete(ctx, nonWatchedTCPRoute))
Expand All @@ -1761,7 +1775,7 @@ func TestNamespaceSelectorsProvider(t *testing.T) {
Name: "watched-tls-route",
},
watchedGateway.Name,
types.NamespacedName{Name: watchedSvc.Name})
types.NamespacedName{Name: watchedSvc.Name}, 443)
require.NoError(t, cli.Create(ctx, watchedTLSRoute))
defer func() {
require.NoError(t, cli.Delete(ctx, watchedTLSRoute))
Expand All @@ -1773,7 +1787,7 @@ func TestNamespaceSelectorsProvider(t *testing.T) {
Name: "non-watched-tls-route",
},
nonWatchedGateway.Name,
types.NamespacedName{Name: nonWatchedNS.Name})
types.NamespacedName{Name: nonWatchedNS.Name}, 443)
require.NoError(t, cli.Create(ctx, nonWatchedTLSRoute))
defer func() {
require.NoError(t, cli.Delete(ctx, nonWatchedTLSRoute))
Expand All @@ -1785,7 +1799,7 @@ func TestNamespaceSelectorsProvider(t *testing.T) {
Name: "watched-udp-route",
},
watchedGateway.Name,
types.NamespacedName{Name: watchedSvc.Name})
types.NamespacedName{Name: watchedSvc.Name}, 80)
require.NoError(t, cli.Create(ctx, watchedUDPRoute))
defer func() {
require.NoError(t, cli.Delete(ctx, watchedUDPRoute))
Expand All @@ -1797,7 +1811,7 @@ func TestNamespaceSelectorsProvider(t *testing.T) {
Name: "non-watched-udp-route",
},
nonWatchedGateway.Name,
types.NamespacedName{Name: nonWatchedNS.Name})
types.NamespacedName{Name: nonWatchedNS.Name}, 80)
require.NoError(t, cli.Create(ctx, nonWatchedUDPRoute))
defer func() {
require.NoError(t, cli.Delete(ctx, nonWatchedUDPRoute))
Expand Down
17 changes: 9 additions & 8 deletions internal/provider/kubernetes/predicates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func TestGatewayClassHasMatchingNamespaceLabels(t *testing.T) {
},
"scheduled-status-test",
types.NamespacedName{Name: "service"},
80,
))
require.Equal(t, tc.expect, res)
})
Expand Down Expand Up @@ -263,7 +264,7 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
configs: []client.Object{
test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName),
sampleGateway,
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}),
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80),
},
endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "other-service"),
expect: false,
Expand All @@ -273,7 +274,7 @@ func TestValidateEndpointSliceForReconcile(t *testing.T) {
configs: []client.Object{
test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName),
sampleGateway,
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}),
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80),
},
endpointSlice: test.GetEndpointSlice(types.NamespacedName{Name: "endpointslice"}, "service"),
expect: true,
Expand Down Expand Up @@ -358,7 +359,7 @@ func TestValidateServiceForReconcile(t *testing.T) {
configs: []client.Object{
test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName),
sampleGateway,
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}),
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80),
},
service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil),
expect: true,
Expand All @@ -370,7 +371,7 @@ func TestValidateServiceForReconcile(t *testing.T) {
name: "route service routes exist but with non-existing gateway reference",
configs: []client.Object{
test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName),
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}),
test.GetHTTPRoute(types.NamespacedName{Name: "httproute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80),
},
service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil),
expect: true,
Expand All @@ -380,7 +381,7 @@ func TestValidateServiceForReconcile(t *testing.T) {
configs: []client.Object{
test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName),
sampleGateway,
test.GetGRPCRoute(types.NamespacedName{Name: "grpcroute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}),
test.GetGRPCRoute(types.NamespacedName{Name: "grpcroute-test"}, "scheduled-status-test", types.NamespacedName{Name: "service"}, 80),
},
service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil),
expect: true,
Expand All @@ -391,7 +392,7 @@ func TestValidateServiceForReconcile(t *testing.T) {
test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName),
sampleGateway,
test.GetTLSRoute(types.NamespacedName{Name: "tlsroute-test"}, "scheduled-status-test",
types.NamespacedName{Name: "service"}),
types.NamespacedName{Name: "service"}, 443),
},
service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil),
expect: true,
Expand All @@ -402,7 +403,7 @@ func TestValidateServiceForReconcile(t *testing.T) {
test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName),
sampleGateway,
test.GetUDPRoute(types.NamespacedName{Name: "udproute-test"}, "scheduled-status-test",
types.NamespacedName{Name: "service"}),
types.NamespacedName{Name: "service"}, 80),
},
service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil),
expect: true,
Expand All @@ -413,7 +414,7 @@ func TestValidateServiceForReconcile(t *testing.T) {
test.GetGatewayClass("test-gc", v1alpha1.GatewayControllerName),
sampleGateway,
test.GetTCPRoute(types.NamespacedName{Name: "tcproute-test"}, "scheduled-status-test",
types.NamespacedName{Name: "service"}),
types.NamespacedName{Name: "service"}, 80),
},
service: test.GetService(types.NamespacedName{Name: "service"}, nil, nil),
expect: true,
Expand Down
Loading