From dfa0d8f2170280d4634c32319b13d4d12e0049c9 Mon Sep 17 00:00:00 2001 From: sh2 Date: Sat, 13 Jan 2024 20:17:20 +0800 Subject: [PATCH] fix: add validation for envoy gateway watch mode field and update doc (#2431) * add validate for envoy gateway watch mode field and update doc Signed-off-by: Shawnh2 * resolve ci and update doc types Signed-off-by: Shawnh2 * Update api/v1alpha1/envoygateway_types.go Co-authored-by: Arko Dasgupta Signed-off-by: sh2 * resolve gen-check Signed-off-by: Shawnh2 --------- Signed-off-by: Shawnh2 Signed-off-by: sh2 Co-authored-by: Arko Dasgupta Co-authored-by: Xunzhuo --- api/v1alpha1/envoygateway_types.go | 6 +- .../validation/envoygateway_validate.go | 16 +++- .../validation/envoygateway_validate_test.go | 94 ++++++++++++++++++- site/content/en/latest/api/extension_types.md | 4 +- .../content/en/latest/user/deployment-mode.md | 27 ++++-- 5 files changed, 131 insertions(+), 16 deletions(-) diff --git a/api/v1alpha1/envoygateway_types.go b/api/v1alpha1/envoygateway_types.go index 66abfdec9c8..0b5e33b2018 100644 --- a/api/v1alpha1/envoygateway_types.go +++ b/api/v1alpha1/envoygateway_types.go @@ -219,13 +219,13 @@ type KubernetesWatchMode struct { // Namespaces holds the list of namespaces that Envoy Gateway will watch for namespaced scoped // resources such as Gateway, HTTPRoute and Service. // Note that Envoy Gateway will continue to reconcile relevant cluster scoped resources such as - // GatewayClass that it is linked to. Precisely one of Namespaces and NamespaceSelectors must be set + // GatewayClass that it is linked to. Precisely one of Namespaces and NamespaceSelectors must be set. Namespaces []string `json:"namespaces,omitempty"` // NamespaceSelectors holds a list of labels that namespaces have to have in order to be watched. // Note this doesn't set the informer to watch the namespaces with the given labels. Informer still - // watches all namespaces. But the events for objects whois namespce have no given labels - // will be filtered out. Precisely one of Namespaces and NamespaceSelectors must be set + // watches all namespaces. But the events for objects whose namespace do not match given labels + // will be filtered out. Precisely one of Namespaces and NamespaceSelectors must be set. NamespaceSelectors []string `json:"namespaceSelectors,omitempty"` } diff --git a/api/v1alpha1/validation/envoygateway_validate.go b/api/v1alpha1/validation/envoygateway_validate.go index 657cb9ee73d..db712dd43ac 100644 --- a/api/v1alpha1/validation/envoygateway_validate.go +++ b/api/v1alpha1/validation/envoygateway_validate.go @@ -15,7 +15,7 @@ import ( "github.com/envoyproxy/gateway/api/v1alpha1" ) -// Validate validates the provided EnvoyGateway. +// ValidateEnvoyGateway validates the provided EnvoyGateway. func ValidateEnvoyGateway(eg *v1alpha1.EnvoyGateway) error { switch { case eg == nil: @@ -28,6 +28,20 @@ func ValidateEnvoyGateway(eg *v1alpha1.EnvoyGateway) error { return errors.New("provider is unspecified") case eg.Provider.Type != v1alpha1.ProviderTypeKubernetes: return fmt.Errorf("unsupported provider %v", eg.Provider.Type) + case eg.Provider.Kubernetes != nil && eg.Provider.Kubernetes.Watch != nil: + watch := eg.Provider.Kubernetes.Watch + switch watch.Type { + case v1alpha1.KubernetesWatchModeTypeNamespaces: + if len(watch.Namespaces) == 0 { + return errors.New("namespaces should be specified when envoy gateway watch mode is 'Namespaces'") + } + case v1alpha1.KubernetesWatchModeTypeNamespaceSelectors: + if len(watch.NamespaceSelectors) == 0 { + return errors.New("namespaceSelectors should be specified when envoy gateway watch mode is 'NamespaceSelectors'") + } + default: + return errors.New("envoy gateway watch mode invalid, should be 'Namespaces' or 'NamespaceSelectors'") + } case eg.Logging != nil && len(eg.Logging.Level) != 0: level := eg.Logging.Level for component, logLevel := range level { diff --git a/api/v1alpha1/validation/envoygateway_validate_test.go b/api/v1alpha1/validation/envoygateway_validate_test.go index b9c07981445..260a9704aae 100644 --- a/api/v1alpha1/validation/envoygateway_validate_test.go +++ b/api/v1alpha1/validation/envoygateway_validate_test.go @@ -415,7 +415,8 @@ func TestValidateEnvoyGateway(t *testing.T) { }, }, expect: false, - }, { + }, + { name: "valid gateway metrics sink", eg: &v1alpha1.EnvoyGateway{ EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ @@ -438,7 +439,8 @@ func TestValidateEnvoyGateway(t *testing.T) { }, }, expect: true, - }, { + }, + { name: "invalid gateway metrics sink", eg: &v1alpha1.EnvoyGateway{ EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ @@ -457,6 +459,94 @@ func TestValidateEnvoyGateway(t *testing.T) { }, expect: false, }, + { + name: "invalid gateway watch mode", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: &v1alpha1.EnvoyGatewayProvider{ + Type: v1alpha1.ProviderTypeKubernetes, + Kubernetes: &v1alpha1.EnvoyGatewayKubernetesProvider{ + Watch: &v1alpha1.KubernetesWatchMode{ + Type: "foobar", + }, + }, + }, + }, + }, + expect: false, + }, + { + name: "happy namespaces must be set when watch mode is Namespaces", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: &v1alpha1.EnvoyGatewayProvider{ + Type: v1alpha1.ProviderTypeKubernetes, + Kubernetes: &v1alpha1.EnvoyGatewayKubernetesProvider{ + Watch: &v1alpha1.KubernetesWatchMode{ + Type: v1alpha1.KubernetesWatchModeTypeNamespaces, + Namespaces: []string{"foo"}, + }, + }, + }, + }, + }, + expect: true, + }, + { + name: "fail namespaces is not be set when watch mode is Namespaces", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: &v1alpha1.EnvoyGatewayProvider{ + Type: v1alpha1.ProviderTypeKubernetes, + Kubernetes: &v1alpha1.EnvoyGatewayKubernetesProvider{ + Watch: &v1alpha1.KubernetesWatchMode{ + Type: v1alpha1.KubernetesWatchModeTypeNamespaces, + NamespaceSelectors: []string{"foo"}, + }, + }, + }, + }, + }, + expect: false, + }, + { + name: "happy namespaceSelectors must be set when watch mode is NamespaceSelectors", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: &v1alpha1.EnvoyGatewayProvider{ + Type: v1alpha1.ProviderTypeKubernetes, + Kubernetes: &v1alpha1.EnvoyGatewayKubernetesProvider{ + Watch: &v1alpha1.KubernetesWatchMode{ + Type: v1alpha1.KubernetesWatchModeTypeNamespaceSelectors, + NamespaceSelectors: []string{"foo"}, + }, + }, + }, + }, + }, + expect: true, + }, + { + name: "fail namespaceSelectors is not be set when watch mode is NamespaceSelectors", + eg: &v1alpha1.EnvoyGateway{ + EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{ + Gateway: v1alpha1.DefaultGateway(), + Provider: &v1alpha1.EnvoyGatewayProvider{ + Type: v1alpha1.ProviderTypeKubernetes, + Kubernetes: &v1alpha1.EnvoyGatewayKubernetesProvider{ + Watch: &v1alpha1.KubernetesWatchMode{ + Type: v1alpha1.KubernetesWatchModeTypeNamespaceSelectors, + }, + }, + }, + }, + }, + expect: false, + }, } for _, tc := range testCases { diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index def8eba49b2..20f109bb791 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -1227,8 +1227,8 @@ _Appears in:_ | Field | Description | | --- | --- | | `type` _[KubernetesWatchModeType](#kuberneteswatchmodetype)_ | Type indicates what watch mode to use. KubernetesWatchModeTypeNamespaces and KubernetesWatchModeTypeNamespaceSelectors are currently supported By default, when this field is unset or empty, Envoy Gateway will watch for input namespaced resources from all namespaces. | -| `namespaces` _string array_ | Namespaces holds the list of namespaces that Envoy Gateway will watch for namespaced scoped resources such as Gateway, HTTPRoute and Service. Note that Envoy Gateway will continue to reconcile relevant cluster scoped resources such as GatewayClass that it is linked to. Precisely one of Namespaces and NamespaceSelectors must be set | -| `namespaceSelectors` _string array_ | NamespaceSelectors holds a list of labels that namespaces have to have in order to be watched. Note this doesn't set the informer to watch the namespaces with the given labels. Informer still watches all namespaces. But the events for objects whois namespce have no given labels will be filtered out. Precisely one of Namespaces and NamespaceSelectors must be set | +| `namespaces` _string array_ | Namespaces holds the list of namespaces that Envoy Gateway will watch for namespaced scoped resources such as Gateway, HTTPRoute and Service. Note that Envoy Gateway will continue to reconcile relevant cluster scoped resources such as GatewayClass that it is linked to. Precisely one of Namespaces and NamespaceSelectors must be set. | +| `namespaceSelectors` _string array_ | NamespaceSelectors holds a list of labels that namespaces have to have in order to be watched. Note this doesn't set the informer to watch the namespaces with the given labels. Informer still watches all namespaces. But the events for objects whose namespace do not match given labels will be filtered out. Precisely one of Namespaces and NamespaceSelectors must be set. | #### KubernetesWatchModeType diff --git a/site/content/en/latest/user/deployment-mode.md b/site/content/en/latest/user/deployment-mode.md index 88428796359..cc45f61764e 100644 --- a/site/content/en/latest/user/deployment-mode.md +++ b/site/content/en/latest/user/deployment-mode.md @@ -16,8 +16,8 @@ in different namespaces, linking a GatewayClass to each of them. * The default deployment model is - Envoy Gateway **watches** for resources such a `Service` & `HTTPRoute` in **all** namespaces and **creates** managed data plane resources such as EnvoyProxy `Deployment` in the **namespace where Envoy Gateway is running**. -* Envoy Gateway also supports **Namespaced** deployment mode, you can watch resources in the specific namespaces by assigning -`EnvoyGateway.provider.kubernetes.watch.namespaces` and **creates** managed data plane resources in the **namespace where Envoy Gateway is running**. +* Envoy Gateway also supports [Namespaced deployment mode][], you can watch resources in the specific namespaces by assigning +`EnvoyGateway.provider.kubernetes.watch.namespaces` or `EnvoyGateway.provider.kubernetes.watch.namespaceSelectors` and **creates** managed data plane resources in the **namespace where Envoy Gateway is running**. * Support for alternate deployment modes is being tracked [here][issue1117]. ### Multi-tenancy @@ -30,8 +30,13 @@ by the `marketing` and `product` teams in separate namespaces. * Lets deploy Envoy Gateway in the `marketing` namespace and also watch resources only in this namespace. We are also setting the controller name to a unique string here `gateway.envoyproxy.io/marketing-gatewayclass-controller`. -``` -helm install --set config.envoyGateway.gateway.controllerName=gateway.envoyproxy.io/marketing-gatewayclass-controller --set config.envoyGateway.provider.kubernetes.watch.namespaces={marketing} eg-marketing oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n marketing --create-namespace +```shell +helm install \ +--set config.envoyGateway.gateway.controllerName=gateway.envoyproxy.io/marketing-gatewayclass-controller \ +--set config.envoyGateway.provider.kubernetes.watch.type=Namespaces \ +--set config.envoyGateway.provider.kubernetes.watch.namespaces={marketing} \ +eg-marketing oci://docker.io/envoyproxy/gateway-helm \ +--version v0.0.0-latest -n marketing --create-namespace ``` Lets create a `GatewayClass` linked to the marketing team's Envoy Gateway controller, and as well other resources linked to it, so the `backend` application operated by this team can be exposed to external clients. @@ -148,7 +153,7 @@ kubectl -n marketing port-forward service/${ENVOY_SERVICE} 8888:8080 & curl --verbose --header "Host: www.marketing.example.com" http://localhost:8888/get ``` -``` +```console * Trying 127.0.0.1:8888... * Connected to localhost (127.0.0.1) port 8888 (#0) > GET /get HTTP/1.1 @@ -203,8 +208,13 @@ Handling connection for 8888 * Lets deploy Envoy Gateway in the `product` namespace and also watch resources only in this namespace. -``` -helm install --set config.envoyGateway.gateway.controllerName=gateway.envoyproxy.io/product-gatewayclass-controller --set config.envoyGateway.provider.kubernetes.watch.namespaces={product} eg-product oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n product --create-namespace +```shell +helm install \ +--set config.envoyGateway.gateway.controllerName=gateway.envoyproxy.io/product-gatewayclass-controller \ +--set config.envoyGateway.provider.kubernetes.watch.type=Namespaces \ +--set config.envoyGateway.provider.kubernetes.watch.namespaces={product} \ +eg-product oci://docker.io/envoyproxy/gateway-helm \ +--version v0.0.0-latest -n product --create-namespace ``` Lets create a `GatewayClass` linked to the product team's Envoy Gateway controller, and as well other resources linked to it, so the `backend` application operated by this team can be exposed to external clients. @@ -381,7 +391,7 @@ and the product team's data plane. curl --verbose --header "Host: www.marketing.example.com" http://localhost:8889/get ``` -``` +```console * Trying 127.0.0.1:8889... * Connected to localhost (127.0.0.1) port 8889 (#0) > GET /get HTTP/1.1 @@ -400,5 +410,6 @@ Handling connection for 8889 ``` [GatewayClass]: https://gateway-api.sigs.k8s.io/api-types/gatewayclass/ +[Namespaced deployment mode]: ../../api/extension_types#kuberneteswatchmode [issue1231]: https://github.com/envoyproxy/gateway/issues/1231 [issue1117]: https://github.com/envoyproxy/gateway/issues/1117