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

helm: support webhook.enablePolicyApi #9632

Merged
merged 13 commits into from
Jun 18, 2024
8 changes: 8 additions & 0 deletions changelog/v1.18.0-beta1/helm-policy-validation-api.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
changelog:
- type: HELM
issueLink: https://github.com/solo-io/solo-projects/issues/6352
resolvesIssue: false
description: >-
Introduce `gateway.validation.webhook.enablePolicyApi` which controls whether or not RouteOptions and
VirtualHostOptions CRs are subject to validation. By default, this value is true.
The validation of these Policy APIs only runs if the Kubernetes Gateway integration is enabled (`kubeGateway.enabled`).
1 change: 1 addition & 0 deletions docs/content/reference/values.txt
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@
|gateway.validation.webhook.timeoutSeconds|int||the timeout for the webhook, defaults to 10|
|gateway.validation.webhook.extraAnnotations.NAME|string||extra annotations to add to the webhook|
|gateway.validation.webhook.skipDeleteValidationResources[]|string||resource types in this list will not use webhook valdaition for DELETEs. Use '*' to skip validation for all resources. Valid values are 'virtualservices', 'routetables','upstreams', 'secrets', 'ratelimitconfigs', and '*'. Invalid values will be accepted but will not be used.|
|gateway.validation.webhook.enablePolicyApi|bool|true|enable validation of Policy Api resources (RouteOptions, VirtualHostOptions) (default: true). NOTE: This only applies if the Kubernetes Gateway Integration is also enabled (kubeGateway.enabled).|
|gateway.validation.webhook.kubeResourceOverride.NAME|interface||override fields in the generated resource by specifying the yaml structure to override under the top-level key.|
|gateway.validation.validationServerGrpcMaxSizeBytes|int|104857600|gRPC max message size in bytes for the gloo validation server|
|gateway.validation.livenessProbeEnabled|bool||Set to true to enable a liveness probe for the gateway (default is false). You must also set the 'Probes' value to true.|
Expand Down
6 changes: 6 additions & 0 deletions install/helm/gloo/generate/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,12 @@ type Webhook struct {
TimeoutSeconds *int `json:"timeoutSeconds,omitempty" desc:"the timeout for the webhook, defaults to 10"`
ExtraAnnotations map[string]string `json:"extraAnnotations,omitempty" desc:"extra annotations to add to the webhook"`
SkipDeleteValidationResources []string `json:"skipDeleteValidationResources,omitempty" desc:"resource types in this list will not use webhook valdaition for DELETEs. Use '*' to skip validation for all resources. Valid values are 'virtualservices', 'routetables','upstreams', 'secrets', 'ratelimitconfigs', and '*'. Invalid values will be accepted but will not be used."`
// EnablePolicyApi provides granular access to users to opt-out of Policy validation
// There are some known race conditions in our Gloo Gateway processes resource references,
// even when allowWarnings=true: https://github.com/solo-io/solo-projects/issues/6321
// As a result, this is intended as a short-term solution to provide users a way to opt-out of Policy API validation.
// The desired long-term strategy is that our validation logic is stable, and users can leverage it
EnablePolicyApi *bool `json:"enablePolicyApi,omitempty" desc:"enable validation of Policy Api resources (RouteOptions, VirtualHostOptions) (default: true). NOTE: This only applies if the Kubernetes Gateway Integration is also enabled (kubeGateway.enabled)."`
*KubeResourceOverride
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ webhooks:
path: "/validation"
caBundle: "" # update manually or use certgen job or cert-manager's ca-injector
rules:
{{- if .Values.kubeGateway.enabled }}
{{- if and .Values.kubeGateway.enabled .Values.gateway.validation.webhook.enablePolicyApi }}
- operations: [ "CREATE", "UPDATE" ]
# RouteOption and VirtualHostOption DELETEs are not supported.
# Their validation is currently limited to usage as Kube Gateway API Policies
Expand All @@ -33,7 +33,7 @@ webhooks:
apiGroups: ["gateway.solo.io"]
apiVersions: ["v1"]
resources: ["routeoptions", "virtualhostoptions"]
{{- end }}{{/* if .Values.kubeGateway.enabled */}}
{{- end }}{{/* if and .Values.kubeGateway.enabled .Values.gateway.validation.webhook.enablePolicyApi */}}
- operations: {{ include "gloo.webhookvalidation.operationsForResource" (list "virtualservices" .Values.gateway.validation.webhook.skipDeleteValidationResources) }}
apiGroups: ["gateway.solo.io"]
apiVersions: ["v1"]
Expand Down
4 changes: 4 additions & 0 deletions install/helm/gloo/values-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ gateway:
enabled: true
disableHelmHook: false
extraAnnotations: {}
# We have learned that defaulting validation behavior leads to unintentional usage of it
# https://github.com/solo-io/gloo/issues/9309
# As a result, for our Policy API, we default it to on, and provide users the way to opt-out of it
enablePolicyApi: true
certGenJob:
enabled: true
image:
Expand Down
118 changes: 118 additions & 0 deletions install/test/5-gateway-validation-webhook-configuration_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package test

import (
"encoding/json"
"fmt"
"regexp"
"strconv"
"strings"

"github.com/golang/protobuf/ptypes/wrappers"
"github.com/onsi/gomega/types"
v1 "k8s.io/api/admissionregistration/v1"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
gloostringutils "github.com/solo-io/gloo/pkg/utils/stringutils"
Expand All @@ -15,6 +20,10 @@ import (
"sigs.k8s.io/yaml"
)

const (
validatingWebhookConfigurationKind = "ValidatingWebhookConfiguration"
)

var _ = Describe("WebhookValidationConfiguration helm test", func() {
var allTests = func(rendererTestCase renderTestCase) {

Expand Down Expand Up @@ -55,6 +64,115 @@ var _ = Describe("WebhookValidationConfiguration helm test", func() {
Entry("all", []string{"*"}, 5),
Entry("empty", []string{}, 0),
)

Context("enablePolicyApi", func() {

// containPolicyApiOperation returns a GomegaMatcher which will assert that a provided ValidatingWebhookConfiguration
// contains the Rule that includes the Policy APIs
containPolicyApiOperation := func() types.GomegaMatcher {
policyApiOperation := v1.RuleWithOperations{
Operations: []v1.OperationType{
v1.Create,
v1.Update,
},
Rule: v1.Rule{
APIGroups: []string{"gateway.solo.io"},
APIVersions: []string{"v1"},
Resources: []string{"routeoptions", "virtualhostoptions"},
},
}
return WithTransform(func(config *v1.ValidatingWebhookConfiguration) []v1.RuleWithOperations {
if config == nil {
return nil
}
return config.Webhooks[0].Rules
}, ContainElement(policyApiOperation))
}

type enablePolicyApiCase struct {
enableKubeGatewayApi *wrappers.BoolValue
enablePolicyApi *wrappers.BoolValue
expectedWebhookConfiguration types.GomegaMatcher
}

DescribeTable("respects helm values",
func(testCase enablePolicyApiCase) {
var valuesArgs []string
if testCase.enableKubeGatewayApi != nil {
valuesArgs = append(valuesArgs, fmt.Sprintf(`kubeGateway.enabled=%t`, testCase.enableKubeGatewayApi.GetValue()))
}
if testCase.enablePolicyApi != nil {
valuesArgs = append(valuesArgs, fmt.Sprintf(`gateway.validation.webhook.enablePolicyApi=%t`, testCase.enablePolicyApi.GetValue()))
}

prepareMakefile(namespace, helmValues{
valuesArgs: valuesArgs,
})

testManifest.ExpectUnstructured(
validatingWebhookConfigurationKind,
"", // ValidatingWebhookConfiguration is cluster-scoped
fmt.Sprintf("gloo-gateway-validation-webhook-%s", namespace),
).To(WithTransform(func(unstructuredVwc *unstructured.Unstructured) *v1.ValidatingWebhookConfiguration {
// convert the unstructured validating webhook configuration to a structured object
if unstructuredVwc == nil {
return nil
}

rawJson, err := json.Marshal(unstructuredVwc.Object)
if err != nil {
return nil
}

var structuredVwc *v1.ValidatingWebhookConfiguration
err = json.Unmarshal(rawJson, &structuredVwc)
if err != nil {
return nil
}

return structuredVwc
}, testCase.expectedWebhookConfiguration))
},
Entry("unset", enablePolicyApiCase{
enableKubeGatewayApi: nil,
enablePolicyApi: nil,
expectedWebhookConfiguration: Not(containPolicyApiOperation()),
}),
Entry("enableKubeGatewayApi=false,", enablePolicyApiCase{
enableKubeGatewayApi: &wrappers.BoolValue{Value: false},
enablePolicyApi: nil,
expectedWebhookConfiguration: Not(containPolicyApiOperation()),
}),
Entry("enableKubeGatewayApi=true,", enablePolicyApiCase{
enableKubeGatewayApi: &wrappers.BoolValue{Value: true},
enablePolicyApi: nil, // default is true
expectedWebhookConfiguration: containPolicyApiOperation(),
}),
Entry("enableKubeGatewayApi=true, enablePolicyApi=true", enablePolicyApiCase{
enableKubeGatewayApi: &wrappers.BoolValue{Value: true},
enablePolicyApi: &wrappers.BoolValue{Value: true},
expectedWebhookConfiguration: containPolicyApiOperation(),
}),
// This is the critical test case, which demonstrates that a user can enabled the K8s Gateway Integration,
// but disable validation for the Policy APIs
Entry("enableKubeGatewayApi=false, enablePolicyApi=true", enablePolicyApiCase{
enableKubeGatewayApi: &wrappers.BoolValue{Value: false},
enablePolicyApi: &wrappers.BoolValue{Value: true},
expectedWebhookConfiguration: Not(containPolicyApiOperation()),
}),
Entry("enableKubeGatewayApi=false, enablePolicyApi=true", enablePolicyApiCase{
enableKubeGatewayApi: &wrappers.BoolValue{Value: false},
enablePolicyApi: &wrappers.BoolValue{Value: true},
expectedWebhookConfiguration: Not(containPolicyApiOperation()),
}),
Entry("enableKubeGatewayApi=false, enablePolicyApi=false", enablePolicyApiCase{
enableKubeGatewayApi: &wrappers.BoolValue{Value: false},
enablePolicyApi: &wrappers.BoolValue{Value: false},
expectedWebhookConfiguration: Not(containPolicyApiOperation()),
}),
)
})

}
runTests(allTests)
})
Expand Down
Loading