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

fix: add validation for envoy gateway watch mode field and update doc #2431

Merged
merged 5 commits into from
Jan 13, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
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 whois namespace have no given labels
shawnh2 marked this conversation as resolved.
Show resolved Hide resolved
// 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 @@ -1226,8 +1226,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 whois namespace have no 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