Skip to content

Commit

Permalink
fix: add validation for envoy gateway watch mode field and update doc (
Browse files Browse the repository at this point in the history
…envoyproxy#2431)

* add validate for envoy gateway watch mode field and update doc

Signed-off-by: Shawnh2 <[email protected]>

* resolve ci and update doc types

Signed-off-by: Shawnh2 <[email protected]>

* Update api/v1alpha1/envoygateway_types.go

Co-authored-by: Arko Dasgupta <[email protected]>
Signed-off-by: sh2 <[email protected]>

* resolve gen-check

Signed-off-by: Shawnh2 <[email protected]>

---------

Signed-off-by: Shawnh2 <[email protected]>
Signed-off-by: sh2 <[email protected]>
Co-authored-by: Arko Dasgupta <[email protected]>
Co-authored-by: Xunzhuo <[email protected]>
  • Loading branch information
3 people authored Jan 13, 2024
1 parent cf83f5b commit dfa0d8f
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 16 deletions.
6 changes: 3 additions & 3 deletions api/v1alpha1/envoygateway_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}

Expand Down
16 changes: 15 additions & 1 deletion api/v1alpha1/validation/envoygateway_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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 {
Expand Down
94 changes: 92 additions & 2 deletions api/v1alpha1/validation/envoygateway_validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,8 @@ func TestValidateEnvoyGateway(t *testing.T) {
},
},
expect: false,
}, {
},
{
name: "valid gateway metrics sink",
eg: &v1alpha1.EnvoyGateway{
EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{
Expand All @@ -438,7 +439,8 @@ func TestValidateEnvoyGateway(t *testing.T) {
},
},
expect: true,
}, {
},
{
name: "invalid gateway metrics sink",
eg: &v1alpha1.EnvoyGateway{
EnvoyGatewaySpec: v1alpha1.EnvoyGatewaySpec{
Expand All @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions site/content/en/latest/api/extension_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 19 additions & 8 deletions site/content/en/latest/user/deployment-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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

0 comments on commit dfa0d8f

Please sign in to comment.