From b9f594ca2cd3b189268e0ed44869ed24737fe9e7 Mon Sep 17 00:00:00 2001 From: zirain Date: Wed, 9 Oct 2024 20:45:03 +0800 Subject: [PATCH 01/56] chore: upgrade test should use VERSION by default (#4393) * chore: upgrade test should use VERSION by default Signed-off-by: zirain * update Signed-off-by: zirain --------- Signed-off-by: zirain --- test/e2e/tests/eg_upgrade.go | 96 +++++++++++++++++++++--------------- tools/make/kube.mk | 2 +- 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/test/e2e/tests/eg_upgrade.go b/test/e2e/tests/eg_upgrade.go index 385952bd2a5..4ca4db15637 100644 --- a/test/e2e/tests/eg_upgrade.go +++ b/test/e2e/tests/eg_upgrade.go @@ -12,14 +12,17 @@ import ( "context" "fmt" "os" + "strings" "testing" "time" + "github.com/stretchr/testify/require" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" "helm.sh/helm/v3/pkg/chart/loader" "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/kube" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -50,11 +53,11 @@ var EGUpgradeTest = suite.ConformanceTest{ chartPath := "../../../charts/gateway-helm" relName := "eg" depNS := "envoy-gateway-system" - lastVersionTag := os.Getenv("last_version_tag") + lastVersionTag := os.Getenv("LAST_VERSION_TAG") if lastVersionTag == "" { - // Use v1.0.2 instead of v1.1.2 due to https://github.com/envoyproxy/gateway/issues/4336 - lastVersionTag = "v1.0.2" // Default version tag if not specified + lastVersionTag = "v1.1.2" // Default version tag if not specified } + t.Logf("Upgrading from version: %s", lastVersionTag) // Uninstall the current version of EG relNamespace := "envoy-gateway-system" @@ -105,18 +108,25 @@ var EGUpgradeTest = suite.ConformanceTest{ suite.Applier.GatewayClass = suite.GatewayClassName suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, suite.BaseManifests, suite.Cleanup) + // verify latestVersion is working + kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{depNS}) + + // let's make sure the gateway is up and running + ns := "gateway-upgrade-infra" + gwNN := types.NamespacedName{Name: "ha-gateway", Namespace: ns} + _, err = kubernetes.WaitForGatewayAddress(t, suite.Client, suite.TimeoutConfig, kubernetes.GatewayRef{ + NamespacedName: gwNN, + }) + require.NoErrorf(t, err, "timed out waiting for Gateway address to be assigned") + + // Apply the test manifests for _, manifestLocation := range []string{"testdata/eg-upgrade.yaml"} { tlog.Logf(t, "Applying %s", manifestLocation) suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, manifestLocation, true) } // wait for everything to startup - kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{depNS}) - - // verify latestVersion is working - ns := "gateway-upgrade-infra" routeNN := types.NamespacedName{Name: "http-backend-eg-upgrade", Namespace: ns} - gwNN := types.NamespacedName{Name: "ha-gateway", Namespace: ns} gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) kubernetes.NamespacesMustBeReady(t, suite.Client, suite.TimeoutConfig, []string{depNS}) @@ -266,47 +276,53 @@ func updateChartCRDs(actionConfig *action.Configuration, gatewayChart *chart.Cha return err } -// TODO: proper migration framework required -func migrateChartCRDs(actionConfig *action.Configuration, gatewayChart *chart.Chart, timeout time.Duration) error { +func migrateChartCRDs(actionConfig *action.Configuration, gatewayChart *chart.Chart, _ time.Duration) error { crds, err := extractCRDs(actionConfig, gatewayChart) if err != nil { return err } + // https: //gateway-api.sigs.k8s.io/guides/?h=upgrade#v12-upgrade-notes + storedVersionsMap := map[string]string{ + "referencegrants.gateway.networking.k8s.io": "v1beta1", + "grpcroutes.gateway.networking.k8s.io": "v1", + } + + restCfg, err := actionConfig.RESTClientGetter.ToRESTConfig() + if err != nil { + return err + } + + cli, err := client.New(restCfg, client.Options{}) + if err != nil { + return err + } + for _, crd := range crds { - if crd.Name == "backendtlspolicies.gateway.networking.k8s.io" || - crd.Name == "grpcroutes.gateway.networking.k8s.io" { - newVersion, err := getGWAPIVersion(crd.Object) + storedVersion, ok := storedVersionsMap[crd.Name] + if !ok { + continue + } + + newVersion, err := getGWAPIVersion(crd.Object) + if err != nil { + return err + } + + if strings.HasPrefix(newVersion, "v1.2.0") { + existingCRD := &apiextensionsv1.CustomResourceDefinition{} + err := cli.Get(context.Background(), types.NamespacedName{Name: crd.Name}, existingCRD) + if kerrors.IsNotFound(err) { + continue + } if err != nil { - return err + return fmt.Errorf("failed to get CRD: %s", err.Error()) } - // https://gateway-api.sigs.k8s.io/guides/?h=upgrade#v11-upgrade-notes - if newVersion == "v1.2.0-rc2" { - helper := resource.NewHelper(crd.Client, crd.Mapping) - existingCRD, err := helper.Get(crd.Namespace, crd.Name) - if kerrors.IsNotFound(err) { - continue - } - // previous version exists - existingVersion, err := getGWAPIVersion(existingCRD) - if err != nil { - return err - } + existingCRD.Status.StoredVersions = []string{storedVersion} - if existingVersion == "v1.0.0" { - // Delete the existing instance of the BTLS and GRPCRoute CRDs - _, errs := actionConfig.KubeClient.Delete([]*resource.Info{crd}) - if errs != nil { - return fmt.Errorf("failed to delete backendtlspolicies: %s", util.MultipleErrors("", errs)) - } - - if kubeClient, ok := actionConfig.KubeClient.(kube.InterfaceExt); ok { - if err := kubeClient.WaitForDelete([]*resource.Info{crd}, timeout); err != nil { - return fmt.Errorf("failed to wait for backendtlspolicies deletion: %s", err.Error()) - } - } - } + if err := cli.Status().Patch(context.Background(), existingCRD, client.MergeFrom(existingCRD)); err != nil { + return fmt.Errorf("failed to patch CRD: %s", err.Error()) } } } @@ -354,7 +370,7 @@ func getGWAPIVersion(object runtime.Object) (string, error) { if ok { return newVersion, nil } - return "", fmt.Errorf("failed to determine Gateway API CRD version") + return "", fmt.Errorf("failed to determine Gateway API CRD version: %v", annotations) } // extractCRDs Extract the CRDs part of the chart diff --git a/tools/make/kube.mk b/tools/make/kube.mk index 2344a3b868d..c659e234787 100644 --- a/tools/make/kube.mk +++ b/tools/make/kube.mk @@ -158,7 +158,7 @@ ifeq ($(E2E_RUN_TEST),) go test $(E2E_TEST_ARGS) ./test/e2e --gateway-class=envoy-gateway --debug=true --cleanup-base-resources=false go test $(E2E_TEST_ARGS) ./test/e2e/merge_gateways --gateway-class=merge-gateways --debug=true --cleanup-base-resources=false go test $(E2E_TEST_ARGS) ./test/e2e/multiple_gc --debug=true --cleanup-base-resources=true - go test $(E2E_TEST_ARGS) ./test/e2e/upgrade --gateway-class=upgrade --debug=true --cleanup-base-resources=$(E2E_CLEANUP) + LAST_VERSION_TAG=$(shell cat VERSION) go test $(E2E_TEST_ARGS) ./test/e2e/upgrade --gateway-class=upgrade --debug=true --cleanup-base-resources=$(E2E_CLEANUP) else go test $(E2E_TEST_ARGS) ./test/e2e --gateway-class=envoy-gateway --debug=true --cleanup-base-resources=$(E2E_CLEANUP) \ --run-test $(E2E_RUN_TEST) From d1cc0ba4372fd7d3f8ede1a1278930e21c22b9f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Oct 2024 20:55:38 +0800 Subject: [PATCH 02/56] build(deps): bump fortio.org/fortio from 1.66.3 to 1.67.1 (#4405) Bumps [fortio.org/fortio](https://github.com/fortio/fortio) from 1.66.3 to 1.67.1. - [Release notes](https://github.com/fortio/fortio/releases) - [Commits](https://github.com/fortio/fortio/compare/v1.66.3...v1.67.1) --- updated-dependencies: - dependency-name: fortio.org/fortio dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 11 ++++++----- go.sum | 22 ++++++++++++---------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index a07ab071892..a34be2541d4 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.23.1 replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16 require ( - fortio.org/fortio v1.66.3 - fortio.org/log v1.16.0 + fortio.org/fortio v1.67.1 + fortio.org/log v1.17.1 github.com/Masterminds/semver/v3 v3.3.0 github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc @@ -72,9 +72,10 @@ require ( cel.dev/expr v0.16.0 // indirect dario.cat/mergo v1.0.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect - fortio.org/cli v1.9.0 // indirect - fortio.org/dflag v1.7.2 // indirect - fortio.org/scli v1.15.2 // indirect + fortio.org/cli v1.9.2 // indirect + fortio.org/dflag v1.7.3 // indirect + fortio.org/safecast v1.0.0 // indirect + fortio.org/scli v1.15.3 // indirect fortio.org/sets v1.2.0 // indirect fortio.org/struct2env v0.4.1 // indirect fortio.org/version v1.0.4 // indirect diff --git a/go.sum b/go.sum index 6e355f58e24..acca7a0b90d 100644 --- a/go.sum +++ b/go.sum @@ -9,16 +9,18 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= fortio.org/assert v1.2.1 h1:48I39urpeDj65RP1KguF7akCjILNeu6vICiYMEysR7Q= fortio.org/assert v1.2.1/go.mod h1:039mG+/iYDPO8Ibx8TrNuJCm2T2SuhwRI3uL9nHTTls= -fortio.org/cli v1.9.0 h1:cPgNHvrjxznmbmwuXSwPqQLKZ+RMW8i0iAOESLjt1aI= -fortio.org/cli v1.9.0/go.mod h1:pk/JBE8LcXtNuo5Yj2bLsVbwPaHo8NWdbstSN0cpbFk= -fortio.org/dflag v1.7.2 h1:lUhXFvDlw4CJj/q7hPv/TC+n/wVoQylzQO6bUg5GQa0= -fortio.org/dflag v1.7.2/go.mod h1:6yO/NIgrWfQH195WbHJ3Y45SCx11ffivQjfx2C/FS1U= -fortio.org/fortio v1.66.3 h1:N/Ic9W2tVoJ5LAcr8ZjcF3mNb2ftCw77ZtKF02jwf8Q= -fortio.org/fortio v1.66.3/go.mod h1:+W7ooyLn/Fp3h0UF9JclFEJN50EOvelO6c+VhCYGwnM= -fortio.org/log v1.16.0 h1:GhU8/9NkYZmEIzvTN/DTMedDAStLJraWUUVUA2EbNDc= -fortio.org/log v1.16.0/go.mod h1:t58Spg9njjymvRioh5F6qKGSupEsnMjXLGWIS1i3khE= -fortio.org/scli v1.15.2 h1:vWXt4QOViXNWy4Gdm7d2FDfptzWD00QiWzYAM/IUF7c= -fortio.org/scli v1.15.2/go.mod h1:XvY2JglgCeeZOIc5CrfBTtcsxkVV8xmGL5ykAcBjEHI= +fortio.org/cli v1.9.2 h1:17eJ8QZPjXHcLBpeCe0QMO/0fj5Bw0ZTxVgL7V9jOqc= +fortio.org/cli v1.9.2/go.mod h1:7r55OoTV8NXcTvJT4boWk8s3I2LP6TMZh/0LLMJEYw0= +fortio.org/dflag v1.7.3 h1:yws+v+/fJ67bYgrgcWpLtgdZPEWkYuwdfqz/WyQ8UXo= +fortio.org/dflag v1.7.3/go.mod h1:O1Pk4lKRolw9wwAGyjTo8IsNyqqNRQGKxPOfpOElMqM= +fortio.org/fortio v1.67.1 h1:KAYyeu6z/01d/QwJm2dCVIadAhd8jNsezJHhlkoOMwU= +fortio.org/fortio v1.67.1/go.mod h1:XfrXH/BJ/hhxBXHj9z8FaqvsBbnf46SLyoWtPgopDlU= +fortio.org/log v1.17.1 h1:YQoGyZBnXTVIs77/nZw7BppwSOIamP3I092PGBenBZs= +fortio.org/log v1.17.1/go.mod h1:t58Spg9njjymvRioh5F6qKGSupEsnMjXLGWIS1i3khE= +fortio.org/safecast v1.0.0 h1:dr3131WPX8iS1pTf76+39WeXbTrerDYLvi9s7Oi3wiY= +fortio.org/safecast v1.0.0/go.mod h1:xZmcPk3vi4kuUFf+tq4SvnlVdwViqf6ZSZl91Jr9Jdg= +fortio.org/scli v1.15.3 h1:XZYONPupGOd1Q68G4aq0vWg9obw0M57sC4snkyiab9w= +fortio.org/scli v1.15.3/go.mod h1:cWJJbXObkF+GsbtPqxE60GFctllOANYS+Yp9PJK0xK8= fortio.org/sets v1.2.0 h1:FBfC7R2xrOJtkcioUbY6WqEzdujuBoZRbSdp1fYF4Kk= fortio.org/sets v1.2.0/go.mod h1:J2BwIxNOLWsSU7IMZUg541kh3Au4JEKHrghVwXs68tE= fortio.org/struct2env v0.4.1 h1:rJludAMO5eBvpWplWEQNqoVDFZr4RWMQX7RUapgZyc0= From 3e0742122543d32d24e31b4295a8e74d6d953ce5 Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Wed, 9 Oct 2024 21:10:38 -0500 Subject: [PATCH 03/56] api: host header rewrite (#4410) * api: host header rewrite Signed-off-by: Guy Daich * review fixes Signed-off-by: Guy Daich --------- Signed-off-by: Guy Daich --- api/v1alpha1/httproutefilter_types.go | 30 ++++++++++ api/v1alpha1/zz_generated.deepcopy.go | 25 +++++++++ ...ateway.envoyproxy.io_httproutefilters.yaml | 24 ++++++++ site/content/en/latest/api/extension_types.md | 30 ++++++++++ site/content/zh/latest/api/extension_types.md | 30 ++++++++++ test/cel-validation/httproutefilter_test.go | 55 +++++++++++++++++++ 6 files changed, 194 insertions(+) diff --git a/api/v1alpha1/httproutefilter_types.go b/api/v1alpha1/httproutefilter_types.go index e0e4755f214..7f56ca07d7c 100644 --- a/api/v1alpha1/httproutefilter_types.go +++ b/api/v1alpha1/httproutefilter_types.go @@ -37,6 +37,12 @@ type HTTPRouteFilterSpec struct { // HTTPURLRewriteFilter define rewrites of HTTP URL components such as path and host type HTTPURLRewriteFilter struct { + // Hostname is the value to be used to replace the Host header value during + // forwarding. + // + // +optional + // +notImplementedHide + Hostname *HTTPHostnameModifier `json:"hostname,omitempty"` // Path defines a path rewrite. // // +optional @@ -53,6 +59,18 @@ const ( RegexHTTPPathModifier HTTPPathModifierType = "ReplaceRegexMatch" ) +// HTTPPathModifierType defines the type of Hostname rewrite. +type HTTPHostnameModifierType string + +const ( + // HeaderHTTPHostnameModifier indicates that the Host header value would be replaced with the value of the header specified in setFromHeader. + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-host-rewrite-header + HeaderHTTPHostnameModifier HTTPHostnameModifierType = "SetFromHeader" + // BackendHTTPHostnameModifier indicates that the Host header value would be replaced by the DNS name of the backend if it exists. + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-auto-host-rewrite + BackendHTTPHostnameModifier HTTPHostnameModifierType = "SetFromBackend" +) + type ReplaceRegexMatch struct { // Pattern matches a regular expression against the value of the HTTP Path.The regex string must // adhere to the syntax documented in https://github.com/google/re2/wiki/Syntax. @@ -91,6 +109,18 @@ type HTTPPathModifier struct { ReplaceRegexMatch *ReplaceRegexMatch `json:"replaceRegexMatch,omitempty"` } +// +kubebuilder:validation:XValidation:message="setFromHeader must be nil if the type is not SetFromHeader",rule="!(has(self.setFromHeader) && self.type != 'SetFromHeader')" +// +kubebuilder:validation:XValidation:message="setFromHeader must be specified for SetFromHeader type",rule="!(!has(self.setFromHeader) && self.type == 'SetFromHeader')" +type HTTPHostnameModifier struct { + // +kubebuilder:validation:Enum=SetFromHeader;SetFromBackend + // +kubebuilder:validation:Required + Type HTTPHostnameModifierType `json:"type"` + + // SetFromHeader is the name of the header whose value would be used to rewrite the Host header + // +optional + SetFromHeader *string `json:"setFromHeader,omitempty"` +} + //+kubebuilder:object:root=true // HTTPRouteFilterList contains a list of HTTPRouteFilter resources. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index b6b944b3a14..ed5df681ad2 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -2709,6 +2709,26 @@ func (in *HTTPExtAuthService) DeepCopy() *HTTPExtAuthService { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPHostnameModifier) DeepCopyInto(out *HTTPHostnameModifier) { + *out = *in + if in.SetFromHeader != nil { + in, out := &in.SetFromHeader, &out.SetFromHeader + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPHostnameModifier. +func (in *HTTPHostnameModifier) DeepCopy() *HTTPHostnameModifier { + if in == nil { + return nil + } + out := new(HTTPHostnameModifier) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPPathModifier) DeepCopyInto(out *HTTPPathModifier) { *out = *in @@ -2835,6 +2855,11 @@ func (in *HTTPTimeout) DeepCopy() *HTTPTimeout { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPURLRewriteFilter) DeepCopyInto(out *HTTPURLRewriteFilter) { *out = *in + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + *out = new(HTTPHostnameModifier) + (*in).DeepCopyInto(*out) + } if in.Path != nil { in, out := &in.Path, &out.Path *out = new(HTTPPathModifier) diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml index 11447cc470e..7a55ec8871f 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml @@ -53,6 +53,30 @@ spec: description: HTTPURLRewriteFilter define rewrites of HTTP URL components such as path and host properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + properties: + setFromHeader: + description: SetFromHeader is the name of the header whose + value would be used to rewrite the Host header + type: string + type: + description: HTTPPathModifierType defines the type of Hostname + rewrite. + enum: + - SetFromHeader + - SetFromBackend + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: setFromHeader must be nil if the type is not SetFromHeader + rule: '!(has(self.setFromHeader) && self.type != ''SetFromHeader'')' + - message: setFromHeader must be specified for SetFromHeader type + rule: '!(!has(self.setFromHeader) && self.type == ''SetFromHeader'')' path: description: Path defines a path rewrite. properties: diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 193e698722c..bc01189920d 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -1938,6 +1938,36 @@ _Appears in:_ | `headersToBackend` | _string array_ | false | HeadersToBackend are the authorization response headers that will be added
to the original client request before sending it to the backend server.
Note that coexisting headers will be overridden.
If not specified, no authorization response headers will be added to the
original client request. | +#### HTTPHostnameModifier + + + + + +_Appears in:_ +- [HTTPURLRewriteFilter](#httpurlrewritefilter) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `type` | _[HTTPHostnameModifierType](#httphostnamemodifiertype)_ | true | | +| `setFromHeader` | _string_ | false | SetFromHeader is the name of the header whose value would be used to rewrite the Host header | + + +#### HTTPHostnameModifierType + +_Underlying type:_ _string_ + +HTTPPathModifierType defines the type of Hostname rewrite. + +_Appears in:_ +- [HTTPHostnameModifier](#httphostnamemodifier) + +| Value | Description | +| ----- | ----------- | +| `SetFromHeader` | HeaderHTTPHostnameModifier indicates that the Host header value would be replaced with the value of the header specified in setFromHeader.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-host-rewrite-header
| +| `SetFromBackend` | BackendHTTPHostnameModifier indicates that the Host header value would be replaced by the DNS name of the backend if it exists.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-auto-host-rewrite
| + + #### HTTPPathModifier diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index 193e698722c..bc01189920d 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -1938,6 +1938,36 @@ _Appears in:_ | `headersToBackend` | _string array_ | false | HeadersToBackend are the authorization response headers that will be added
to the original client request before sending it to the backend server.
Note that coexisting headers will be overridden.
If not specified, no authorization response headers will be added to the
original client request. | +#### HTTPHostnameModifier + + + + + +_Appears in:_ +- [HTTPURLRewriteFilter](#httpurlrewritefilter) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `type` | _[HTTPHostnameModifierType](#httphostnamemodifiertype)_ | true | | +| `setFromHeader` | _string_ | false | SetFromHeader is the name of the header whose value would be used to rewrite the Host header | + + +#### HTTPHostnameModifierType + +_Underlying type:_ _string_ + +HTTPPathModifierType defines the type of Hostname rewrite. + +_Appears in:_ +- [HTTPHostnameModifier](#httphostnamemodifier) + +| Value | Description | +| ----- | ----------- | +| `SetFromHeader` | HeaderHTTPHostnameModifier indicates that the Host header value would be replaced with the value of the header specified in setFromHeader.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-host-rewrite-header
| +| `SetFromBackend` | BackendHTTPHostnameModifier indicates that the Host header value would be replaced by the DNS name of the backend if it exists.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-auto-host-rewrite
| + + #### HTTPPathModifier diff --git a/test/cel-validation/httproutefilter_test.go b/test/cel-validation/httproutefilter_test.go index 67a9e4455bb..7f84deb71e5 100644 --- a/test/cel-validation/httproutefilter_test.go +++ b/test/cel-validation/httproutefilter_test.go @@ -15,6 +15,7 @@ import ( "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) @@ -84,6 +85,60 @@ func TestHTTPRouteFilter(t *testing.T) { "spec.urlRewrite.path.replaceRegexMatch.pattern: Invalid value: \"\": spec.urlRewrite.path.replaceRegexMatch.pattern in body should be at least 1 chars long", }, }, + { + desc: "Valid SetFromHeader", + mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) { + httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{ + URLRewrite: &egv1a1.HTTPURLRewriteFilter{ + Hostname: &egv1a1.HTTPHostnameModifier{ + Type: egv1a1.HeaderHTTPHostnameModifier, + SetFromHeader: ptr.To("foo"), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "Valid SetFromBackend", + mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) { + httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{ + URLRewrite: &egv1a1.HTTPURLRewriteFilter{ + Hostname: &egv1a1.HTTPHostnameModifier{ + Type: egv1a1.BackendHTTPHostnameModifier, + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "invalid SetFromHeader missing settings", + mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) { + httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{ + URLRewrite: &egv1a1.HTTPURLRewriteFilter{ + Hostname: &egv1a1.HTTPHostnameModifier{ + Type: egv1a1.HeaderHTTPHostnameModifier, + }, + }, + } + }, + wantErrors: []string{"spec.urlRewrite.hostname: Invalid value: \"object\": setFromHeader must be specified for SetFromHeader type"}, + }, + { + desc: "invalid SetFromBackend type", + mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) { + httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{ + URLRewrite: &egv1a1.HTTPURLRewriteFilter{ + Hostname: &egv1a1.HTTPHostnameModifier{ + Type: egv1a1.BackendHTTPHostnameModifier, + SetFromHeader: ptr.To("foo"), + }, + }, + } + }, + wantErrors: []string{"spec.urlRewrite.hostname: Invalid value: \"object\": setFromHeader must be nil if the type is not SetFromHeader"}, + }, } for _, tc := range cases { From ae6787e228677ce30274ea80e67d1265da99d1ca Mon Sep 17 00:00:00 2001 From: Rudrakh Panigrahi Date: Thu, 10 Oct 2024 08:23:40 +0530 Subject: [PATCH 04/56] feat: support inverting header matches for rate limit (#4286) Signed-off-by: Rudrakh Panigrahi --- internal/gatewayapi/backendtrafficpolicy.go | 10 +- ...-ratelimit-invalid-distinct-invert.in.yaml | 53 ++++++ ...ratelimit-invalid-distinct-invert.out.yaml | 164 ++++++++++++++++++ ...ackendtrafficpolicy-with-ratelimit.in.yaml | 3 + ...ckendtrafficpolicy-with-ratelimit.out.yaml | 7 + internal/ir/xds.go | 6 + internal/ir/xds_test.go | 19 +- internal/ir/zz_generated.deepcopy.go | 5 + internal/xds/translator/local_ratelimit.go | 6 +- internal/xds/translator/ratelimit.go | 6 +- .../testdata/in/xds-ir/ratelimit.yaml | 21 +++ .../out/xds-ir/ratelimit.clusters.yaml | 17 ++ .../out/xds-ir/ratelimit.endpoints.yaml | 12 ++ .../testdata/out/xds-ir/ratelimit.routes.yaml | 20 +++ 14 files changed, 344 insertions(+), 5 deletions(-) create mode 100644 internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit-invalid-distinct-invert.in.yaml create mode 100644 internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit-invalid-distinct-invert.out.yaml diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index d71d49f32ca..12453ea1826 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -710,8 +710,9 @@ func buildRateLimitRule(rule egv1a1.RateLimitRule) (*ir.RateLimitRule, error) { fallthrough case *header.Type == egv1a1.HeaderMatchExact && header.Value != nil: m := &ir.StringMatch{ - Name: header.Name, - Exact: header.Value, + Name: header.Name, + Exact: header.Value, + Invert: header.Invert, } irRule.HeaderMatches = append(irRule.HeaderMatches, m) case *header.Type == egv1a1.HeaderMatchRegularExpression && header.Value != nil: @@ -721,9 +722,14 @@ func buildRateLimitRule(rule egv1a1.RateLimitRule) (*ir.RateLimitRule, error) { m := &ir.StringMatch{ Name: header.Name, SafeRegex: header.Value, + Invert: header.Invert, } irRule.HeaderMatches = append(irRule.HeaderMatches, m) case *header.Type == egv1a1.HeaderMatchDistinct && header.Value == nil: + if header.Invert != nil && *header.Invert { + return nil, fmt.Errorf("unable to translate rateLimit." + + "Invert is not applicable for distinct header match type") + } m := &ir.StringMatch{ Name: header.Name, Distinct: true, diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit-invalid-distinct-invert.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit-invalid-distinct-invert.in.yaml new file mode 100644 index 00000000000..a1ed0f512cc --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit-invalid-distinct-invert.in.yaml @@ -0,0 +1,53 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway + sectionName: http + rules: + - backendRefs: + - name: service + port: 8080 +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-org-id + type: Distinct + invert: true + limit: + requests: 10 + unit: Hour diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit-invalid-distinct-invert.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit-invalid-distinct-invert.out.yaml new file mode 100644 index 00000000000..4ea1623c867 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit-invalid-distinct-invert.out.yaml @@ -0,0 +1,164 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + spec: + rateLimit: + global: + rules: + - clientSelectors: + - headers: + - invert: true + name: x-org-id + type: Distinct + limit: + requests: 10 + unit: Hour + type: Global + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: 'RateLimit: unable to translate rateLimit.Invert is not applicable + for distinct header match type.' + reason: Invalid + status: "False" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute + namespace: default + spec: + parentRefs: + - name: gateway + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service default/service not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway +xdsIR: + envoy-gateway/gateway: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + metadata: + kind: Gateway + name: gateway + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: grpcroute/default/grpcroute/rule/0 + settings: + - weight: 1 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: true + metadata: + kind: GRPCRoute + name: grpcroute + namespace: default + name: grpcroute/default/grpcroute/rule/0/match/-1/* diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.in.yaml index e4f1fc10c64..f536d9a20bc 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.in.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.in.yaml @@ -83,6 +83,9 @@ backendTrafficPolicies: value: one - name: x-org-id type: Distinct + - name: x-org-id + value: admin + invert: true limit: requests: 10 unit: Hour diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml index 2f7a2d5e8f9..07fa997e109 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-ratelimit.out.yaml @@ -52,6 +52,9 @@ backendTrafficPolicies: value: one - name: x-org-id type: Distinct + - invert: true + name: x-org-id + value: admin limit: requests: 10 unit: Hour @@ -306,6 +309,10 @@ xdsIR: name: x-user-id - distinct: true name: x-org-id + - distinct: false + exact: admin + invert: true + name: x-org-id limit: requests: 10 unit: Hour diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 9750680f387..40e7da917f3 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -47,6 +47,7 @@ var ( ErrDestEndpointUDSPortInvalid = errors.New("field Port must not be specified for Unix Domain Socket address") ErrDestEndpointUDSHostInvalid = errors.New("field Host must not be specified for Unix Domain Socket address") ErrStringMatchConditionInvalid = errors.New("only one of the Exact, Prefix, SafeRegex or Distinct fields must be set") + ErrStringMatchInvertDistinctInvalid = errors.New("only one of the Invert or Distinct fields can be set") ErrStringMatchNameIsEmpty = errors.New("field Name must be specified") ErrDirectResponseStatusInvalid = errors.New("only HTTP status codes 100 - 599 are supported for DirectResponse") ErrRedirectUnsupportedStatus = errors.New("only HTTP status codes 301 and 302 are supported for redirect filters") @@ -1443,6 +1444,8 @@ type StringMatch struct { // Distinct match condition. // Used to match any and all possible unique values encountered within the Name field. Distinct bool `json:"distinct" yaml:"distinct"` + // Invert inverts the final match decision + Invert *bool `json:"invert,omitempty" yaml:"invert,omitempty"` } // Validate the fields within the StringMatch structure @@ -1465,6 +1468,9 @@ func (s StringMatch) Validate() error { if s.Name == "" { errs = errors.Join(errs, ErrStringMatchNameIsEmpty) } + if s.Invert != nil && *s.Invert { + errs = errors.Join(errs, ErrStringMatchInvertDistinctInvalid) + } matchCount++ } diff --git a/internal/ir/xds_test.go b/internal/ir/xds_test.go index 14b624f22f3..5ff9a8736ef 100644 --- a/internal/ir/xds_test.go +++ b/internal/ir/xds_test.go @@ -1182,12 +1182,20 @@ func TestValidateStringMatch(t *testing.T) { want error }{ { - name: "happy", + name: "happy exact", input: StringMatch{ Exact: ptr.To("example"), }, want: nil, }, + { + name: "happy distinct", + input: StringMatch{ + Distinct: true, + Name: "example", + }, + want: nil, + }, { name: "no fields set", input: StringMatch{}, @@ -1202,6 +1210,15 @@ func TestValidateStringMatch(t *testing.T) { }, want: ErrStringMatchConditionInvalid, }, + { + name: "both invert and distinct fields are set", + input: StringMatch{ + Distinct: true, + Name: "example", + Invert: ptr.To(true), + }, + want: ErrStringMatchInvertDistinctInvalid, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 5afb29d12ce..4400b555dd7 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -2573,6 +2573,11 @@ func (in *StringMatch) DeepCopyInto(out *StringMatch) { *out = new(string) **out = **in } + if in.Invert != nil { + in, out := &in.Invert, &out.Invert + *out = new(bool) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StringMatch. diff --git a/internal/xds/translator/local_ratelimit.go b/internal/xds/translator/local_ratelimit.go index 1503758dfb4..ba330e22034 100644 --- a/internal/xds/translator/local_ratelimit.go +++ b/internal/xds/translator/local_ratelimit.go @@ -218,13 +218,17 @@ func buildRouteLocalRateLimits(local *ir.LocalRateLimit) ( StringMatch: buildXdsStringMatcher(match), }, } + expectMatch := true + if match.Invert != nil && *match.Invert { + expectMatch = false + } action := &routev3.RateLimit_Action{ ActionSpecifier: &routev3.RateLimit_Action_HeaderValueMatch_{ HeaderValueMatch: &routev3.RateLimit_Action_HeaderValueMatch{ DescriptorKey: descriptorKey, DescriptorValue: descriptorVal, ExpectMatch: &wrapperspb.BoolValue{ - Value: true, + Value: expectMatch, }, Headers: []*routev3.HeaderMatcher{headerMatcher}, }, diff --git a/internal/xds/translator/ratelimit.go b/internal/xds/translator/ratelimit.go index 8e3e661f9d7..660bc2a7dec 100644 --- a/internal/xds/translator/ratelimit.go +++ b/internal/xds/translator/ratelimit.go @@ -180,13 +180,17 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [ StringMatch: buildXdsStringMatcher(match), }, } + expectMatch := true + if match.Invert != nil && *match.Invert { + expectMatch = false + } action := &routev3.RateLimit_Action{ ActionSpecifier: &routev3.RateLimit_Action_HeaderValueMatch_{ HeaderValueMatch: &routev3.RateLimit_Action_HeaderValueMatch{ DescriptorKey: descriptorKey, DescriptorValue: descriptorVal, ExpectMatch: &wrapperspb.BoolValue{ - Value: true, + Value: expectMatch, }, Headers: []*routev3.HeaderMatcher{headerMatcher}, }, diff --git a/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml b/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml index 271d39cfdcb..2279315caed 100644 --- a/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/ratelimit.yaml @@ -65,3 +65,24 @@ http: - endpoints: - host: "1.2.3.4" port: 50000 + - name: "fourth-route" + hostname: "*" + traffic: + rateLimit: + global: + rules: + - headerMatches: + - name: "x-org-id" + exact: "admin" + invert: true + limit: + requests: 5 + unit: second + pathMatch: + exact: "foo/bar/login" + destination: + name: "fourth-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit.clusters.yaml index 0ba1749076a..427f6d15340 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit.clusters.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit.clusters.yaml @@ -49,6 +49,23 @@ outlierDetection: {} perConnectionBufferLimitBytes: 32768 type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: fourth-route-dest + lbPolicy: LEAST_REQUEST + name: fourth-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS - circuitBreakers: thresholds: - maxRetries: 1024 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit.endpoints.yaml index 475b89a087c..f185af17da7 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit.endpoints.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit.endpoints.yaml @@ -34,3 +34,15 @@ loadBalancingWeight: 1 locality: region: third-route-dest/backend/0 +- clusterName: fourth-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: fourth-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit.routes.yaml index 479c2cd143c..e6e83bc2bfb 100644 --- a/internal/xds/translator/testdata/out/xds-ir/ratelimit.routes.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit.routes.yaml @@ -55,3 +55,23 @@ descriptorValue: rule-0-match--1 upgradeConfigs: - upgradeType: websocket + - match: + path: foo/bar/login + name: fourth-route + route: + cluster: fourth-route-dest + rateLimits: + - actions: + - genericKey: + descriptorKey: fourth-route + descriptorValue: fourth-route + - headerValueMatch: + descriptorKey: rule-0-match-0 + descriptorValue: rule-0-match-0 + expectMatch: false + headers: + - name: x-org-id + stringMatch: + exact: admin + upgradeConfigs: + - upgradeType: websocket From 956586369d6dc59bacf96e56ca214182c367a306 Mon Sep 17 00:00:00 2001 From: zirain Date: Thu, 10 Oct 2024 11:36:36 +0800 Subject: [PATCH 05/56] bump gwapi to 1.2.0 (#4384) * bump gwapi to 1.2.0 Signed-off-by: zirain * use v1.1.2 Signed-off-by: zirain * update Signed-off-by: zirain * fix Signed-off-by: zirain * lint Signed-off-by: zirain * update Signed-off-by: zirain * remove Signed-off-by: zirain --------- Signed-off-by: zirain --- charts/gateway-helm/crds/gatewayapi-crds.yaml | 40 +++++++++---------- go.mod | 2 +- go.sum | 4 +- osv-scanner.toml | 7 ---- 4 files changed, 23 insertions(+), 30 deletions(-) diff --git a/charts/gateway-helm/crds/gatewayapi-crds.yaml b/charts/gateway-helm/crds/gatewayapi-crds.yaml index f19c1adb3b4..57595956279 100644 --- a/charts/gateway-helm/crds/gatewayapi-crds.yaml +++ b/charts/gateway-helm/crds/gatewayapi-crds.yaml @@ -23,8 +23,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 - gateway.networking.k8s.io/bundle-version: v1.2.0-rc2 + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.0 gateway.networking.k8s.io/channel: experimental creationTimestamp: null labels: @@ -524,8 +524,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 - gateway.networking.k8s.io/bundle-version: v1.2.0-rc2 + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.0 gateway.networking.k8s.io/channel: experimental creationTimestamp: null labels: @@ -1153,8 +1153,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 - gateway.networking.k8s.io/bundle-version: v1.2.0-rc2 + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.0 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: gatewayclasses.gateway.networking.k8s.io @@ -1673,8 +1673,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 - gateway.networking.k8s.io/bundle-version: v1.2.0-rc2 + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.0 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: gateways.gateway.networking.k8s.io @@ -4089,8 +4089,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 - gateway.networking.k8s.io/bundle-version: v1.2.0-rc2 + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.0 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: grpcroutes.gateway.networking.k8s.io @@ -6327,8 +6327,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 - gateway.networking.k8s.io/bundle-version: v1.2.0-rc2 + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.0 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: httproutes.gateway.networking.k8s.io @@ -12489,8 +12489,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 - gateway.networking.k8s.io/bundle-version: v1.2.0-rc2 + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.0 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: referencegrants.gateway.networking.k8s.io @@ -12682,8 +12682,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 - gateway.networking.k8s.io/bundle-version: v1.2.0-rc2 + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.0 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: tcproutes.gateway.networking.k8s.io @@ -13427,8 +13427,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 - gateway.networking.k8s.io/bundle-version: v1.2.0-rc2 + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.0 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: tlsroutes.gateway.networking.k8s.io @@ -14235,8 +14235,8 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2997 - gateway.networking.k8s.io/bundle-version: v1.2.0-rc2 + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.0 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: udproutes.gateway.networking.k8s.io diff --git a/go.mod b/go.mod index a34be2541d4..e34c38bed54 100644 --- a/go.mod +++ b/go.mod @@ -56,7 +56,7 @@ require ( k8s.io/kubectl v0.31.1 k8s.io/utils v0.0.0-20240821151609-f90d01438635 sigs.k8s.io/controller-runtime v0.19.0 - sigs.k8s.io/gateway-api v1.2.0-rc2 + sigs.k8s.io/gateway-api v1.2.0 sigs.k8s.io/mcs-api v0.1.0 sigs.k8s.io/yaml v1.4.0 ) diff --git a/go.sum b/go.sum index acca7a0b90d..01eea5533e5 100644 --- a/go.sum +++ b/go.sum @@ -1212,8 +1212,8 @@ sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gE sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI= -sigs.k8s.io/gateway-api v1.2.0-rc2 h1:v7V7JzaBuzwOLWWyyqlkqiqBi3ANBuZGV+uyyKzwmE8= -sigs.k8s.io/gateway-api v1.2.0-rc2/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0= +sigs.k8s.io/gateway-api v1.2.0 h1:LrToiFwtqKTKZcZtoQPTuo3FxhrrhTgzQG0Te+YGSo8= +sigs.k8s.io/gateway-api v1.2.0/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kind v0.8.1/go.mod h1:oNKTxUVPYkV9lWzY6CVMNluVq8cBsyq+UgPJdvA3uu4= diff --git a/osv-scanner.toml b/osv-scanner.toml index 02cdbc6af53..6144707a297 100644 --- a/osv-scanner.toml +++ b/osv-scanner.toml @@ -90,13 +90,6 @@ ecosystem = "Go" license.override = ["BSD-3-Clause"] reason = "Unidentified license, remove once https://github.com/google/deps.dev/issues/86 is resolved" -[[PackageOverrides]] -name = "sigs.k8s.io/gateway-api" -version = "1.2.0-rc2" -ecosystem = "Go" -license.override = ["Apache-2.0"] -reason = "https://github.com/envoyproxy/gateway/actions/runs/11065210699/job/30744231458?pr=4270" - [[PackageOverrides]] name = "sigs.k8s.io/json" version = "0.0.0-20221116044647-bc3834ca7abd" From 4313fd607ba6fad2499aa4e01908a06f9a7b3575 Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Thu, 10 Oct 2024 06:33:26 -0500 Subject: [PATCH 06/56] feat(translator): implement access log types (#4341) * impl: access log types Signed-off-by: Guy Daich * clarify docs Signed-off-by: Guy Daich * add note on default settings override Signed-off-by: Guy Daich * support default log re-enablement Signed-off-by: Guy Daich * fix xds test to contain route Signed-off-by: Guy Daich --------- Signed-off-by: Guy Daich Co-authored-by: zirain --- api/v1alpha1/accesslogging_types.go | 1 - internal/gatewayapi/listener.go | 27 +- .../envoyproxy-accesslog-types.in.yaml | 236 +++++++++ .../envoyproxy-accesslog-types.out.yaml | 460 ++++++++++++++++++ internal/ir/xds.go | 37 +- internal/ir/zz_generated.deepcopy.go | 20 + internal/xds/translator/accesslog.go | 49 +- internal/xds/translator/listener.go | 12 +- .../testdata/in/xds-ir/accesslog-types.yaml | 184 +++++++ .../out/xds-ir/accesslog-types.clusters.yaml | 263 ++++++++++ .../out/xds-ir/accesslog-types.endpoints.yaml | 84 ++++ .../out/xds-ir/accesslog-types.listeners.yaml | 300 ++++++++++++ .../out/xds-ir/accesslog-types.routes.yaml | 20 + site/content/en/latest/api/extension_types.md | 1 + .../tasks/observability/proxy-accesslog.md | 59 +++ site/content/zh/latest/api/extension_types.md | 1 + test/config/gatewayclass.yaml | 9 + test/e2e/tests/accesslog.go | 32 ++ 18 files changed, 1764 insertions(+), 31 deletions(-) create mode 100644 internal/gatewayapi/testdata/envoyproxy-accesslog-types.in.yaml create mode 100644 internal/gatewayapi/testdata/envoyproxy-accesslog-types.out.yaml create mode 100644 internal/xds/translator/testdata/in/xds-ir/accesslog-types.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/accesslog-types.clusters.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/accesslog-types.endpoints.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/accesslog-types.listeners.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/accesslog-types.routes.yaml diff --git a/api/v1alpha1/accesslogging_types.go b/api/v1alpha1/accesslogging_types.go index edc19e97599..31eac69f122 100644 --- a/api/v1alpha1/accesslogging_types.go +++ b/api/v1alpha1/accesslogging_types.go @@ -37,7 +37,6 @@ type ProxyAccessLogSetting struct { // If type is defined, the accesslog settings would apply to the relevant component (as-is). // +kubebuilder:validation:Enum=Listener;Route // +optional - // +notImplementedHide Type *ProxyAccessLogType `json:"type,omitempty"` } diff --git a/internal/gatewayapi/listener.go b/internal/gatewayapi/listener.go index 0c69d7b3097..5c85e561ea1 100644 --- a/internal/gatewayapi/listener.go +++ b/internal/gatewayapi/listener.go @@ -241,7 +241,6 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * }, }, nil } - if envoyproxy.Spec.Telemetry.AccessLog.Disable { return nil, nil } @@ -249,6 +248,16 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * irAccessLog := &ir.AccessLog{} // translate the access log configuration to the IR for i, accessLog := range envoyproxy.Spec.Telemetry.AccessLog.Settings { + var accessLogType *ir.ProxyAccessLogType + if accessLog.Type != nil { + switch *accessLog.Type { + case egv1a1.ProxyAccessLogTypeRoute: + accessLogType = ptr.To(ir.ProxyAccessLogTypeRoute) + case egv1a1.ProxyAccessLogTypeListener: + accessLogType = ptr.To(ir.ProxyAccessLogTypeListener) + } + } + var format egv1a1.ProxyAccessLogFormat if accessLog.Format != nil { format = *accessLog.Format @@ -274,6 +283,16 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * return nil, utilerrors.NewAggregate(errs) } + if len(accessLog.Sinks) == 0 { + al := &ir.TextAccessLog{ + Format: format.Text, + CELMatches: validExprs, + LogType: accessLogType, + Path: "/dev/stdout", + } + irAccessLog.Text = append(irAccessLog.Text, al) + } + for j, sink := range accessLog.Sinks { switch sink.Type { case egv1a1.ProxyAccessLogSinkTypeFile: @@ -287,6 +306,7 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * Format: format.Text, Path: sink.File.Path, CELMatches: validExprs, + LogType: accessLogType, } irAccessLog.Text = append(irAccessLog.Text, al) case egv1a1.ProxyAccessLogFormatTypeJSON: @@ -299,6 +319,7 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * JSON: format.JSON, Path: sink.File.Path, CELMatches: validExprs, + LogType: accessLogType, } irAccessLog.JSON = append(irAccessLog.JSON, al) } @@ -329,6 +350,7 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * Traffic: traffic, Type: sink.ALS.Type, CELMatches: validExprs, + LogType: accessLogType, } if al.Type == egv1a1.ALSEnvoyProxyAccessLogTypeHTTP && sink.ALS.HTTP != nil { @@ -339,7 +361,6 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * } al.HTTP = http } - switch format.Type { case egv1a1.ProxyAccessLogFormatTypeJSON: al.Attributes = format.JSON @@ -367,6 +388,7 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * Settings: ds, }, Traffic: traffic, + LogType: accessLogType, } if len(ds) == 0 { @@ -391,7 +413,6 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * } } } - return irAccessLog, nil } diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-types.in.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-types.in.yaml new file mode 100644 index 00000000000..03977e33075 --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-types.in.yaml @@ -0,0 +1,236 @@ +envoyProxyForGatewayClass: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + namespace: envoy-gateway-system + name: test + spec: + telemetry: + accessLog: + settings: + - type: Route + - type: Listener + - type: Route + format: + type: Text + text: | + this is a route log + sinks: + - type: File + file: + path: /dev/stdout + - type: ALS + als: + logName: accesslog + backendRefs: + - name: envoy-als + namespace: monitoring + port: 9000 + http: + requestHeaders: + - x-client-ip-address + responseHeaders: + - cache-control + responseTrailers: + - expires + type: HTTP + - type: ALS + als: + backendRefs: + - name: envoy-als + namespace: monitoring + port: 9000 + type: TCP + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: "cluster-1" + - type: Listener + format: + type: Text + text: | + this is a listener log + sinks: + - type: File + file: + path: /dev/stdout + - type: ALS + als: + logName: accesslog + backendRefs: + - name: envoy-als + namespace: monitoring + port: 9000 + http: + requestHeaders: + - x-client-ip-address + responseHeaders: + - cache-control + responseTrailers: + - expires + type: HTTP + - type: ALS + als: + backendRefs: + - name: envoy-als + namespace: monitoring + port: 9000 + type: TCP + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: "cluster-1" + - format: + type: Text + text: | + this is a Global log + sinks: + - type: File + file: + path: /dev/stdout + - type: ALS + als: + logName: accesslog + backendRefs: + - name: envoy-als + namespace: monitoring + port: 9000 + http: + requestHeaders: + - x-client-ip-address + responseHeaders: + - cache-control + responseTrailers: + - expires + type: HTTP + - type: ALS + als: + backendRefs: + - name: envoy-als + namespace: monitoring + port: 9000 + type: TCP + - type: OpenTelemetry + openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: "cluster-1" + provider: + type: Kubernetes + kubernetes: + envoyService: + type: LoadBalancer + envoyDeployment: + replicas: 2 + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: "envoyproxy/envoy:distroless-dev" + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + runAsUser: 2000 + allowPrivilegeEscalation: false + pod: + annotations: + key1: val1 + key2: val2 + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: "router" + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 2000 + fsGroupChangePolicy: "OnRootMismatch" + volumes: + - name: certs + secret: + secretName: envoy-cert +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: envoy-gateway + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + type: Exact + value: "/exact" + backendRefs: + - name: service-1 + port: 8080 +services: +- apiVersion: v1 + kind: Service + metadata: + name: envoy-als + namespace: monitoring + spec: + type: ClusterIP + ports: + - name: grpc + port: 9000 + appProtocol: grpc + protocol: TCP + targetPort: 9000 +endpointSlices: +- apiVersion: discovery.k8s.io/v1 + kind: EndpointSlice + metadata: + name: endpointslice-envoy-als + namespace: monitoring + labels: + kubernetes.io/service-name: envoy-als + addressType: IPv4 + ports: + - name: grpc + protocol: TCP + appProtocol: grpc + port: 9090 + endpoints: + - addresses: + - "10.240.0.10" + conditions: + ready: true diff --git a/internal/gatewayapi/testdata/envoyproxy-accesslog-types.out.yaml b/internal/gatewayapi/testdata/envoyproxy-accesslog-types.out.yaml new file mode 100644 index 00000000000..9c2c0d1cf82 --- /dev/null +++ b/internal/gatewayapi/testdata/envoyproxy-accesslog-types.out.yaml @@ -0,0 +1,460 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: envoy-gateway + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + type: Exact + value: /exact + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Service envoy-gateway/service-1 not found + reason: BackendNotFound + status: "False" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + config: + apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: EnvoyProxy + metadata: + creationTimestamp: null + name: test + namespace: envoy-gateway-system + spec: + logging: {} + provider: + kubernetes: + envoyDeployment: + container: + env: + - name: env_a + value: env_a_value + - name: env_b + value: env_b_name + image: envoyproxy/envoy:distroless-dev + resources: + requests: + cpu: 100m + memory: 512Mi + securityContext: + allowPrivilegeEscalation: false + runAsUser: 2000 + pod: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: cloud.google.com/gke-nodepool + operator: In + values: + - router-node + annotations: + key1: val1 + key2: val2 + securityContext: + fsGroup: 2000 + fsGroupChangePolicy: OnRootMismatch + runAsGroup: 3000 + runAsUser: 1000 + tolerations: + - effect: NoSchedule + key: node-type + operator: Exists + value: router + volumes: + - name: certs + secret: + secretName: envoy-cert + replicas: 2 + envoyService: + type: LoadBalancer + type: Kubernetes + telemetry: + accessLog: + settings: + - sinks: null + type: Route + - sinks: null + type: Listener + - format: + text: | + this is a route log + type: Text + sinks: + - file: + path: /dev/stdout + type: File + - als: + backendRefs: + - name: envoy-als + namespace: monitoring + port: 9000 + http: + requestHeaders: + - x-client-ip-address + responseHeaders: + - cache-control + responseTrailers: + - expires + logName: accesslog + type: HTTP + type: ALS + - als: + backendRefs: + - name: envoy-als + namespace: monitoring + port: 9000 + type: TCP + type: ALS + - openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: cluster-1 + type: OpenTelemetry + type: Route + - format: + text: | + this is a listener log + type: Text + sinks: + - file: + path: /dev/stdout + type: File + - als: + backendRefs: + - name: envoy-als + namespace: monitoring + port: 9000 + http: + requestHeaders: + - x-client-ip-address + responseHeaders: + - cache-control + responseTrailers: + - expires + logName: accesslog + type: HTTP + type: ALS + - als: + backendRefs: + - name: envoy-als + namespace: monitoring + port: 9000 + type: TCP + type: ALS + - openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: cluster-1 + type: OpenTelemetry + type: Listener + - format: + text: | + this is a Global log + type: Text + sinks: + - file: + path: /dev/stdout + type: File + - als: + backendRefs: + - name: envoy-als + namespace: monitoring + port: 9000 + http: + requestHeaders: + - x-client-ip-address + responseHeaders: + - cache-control + responseTrailers: + - expires + logName: accesslog + type: HTTP + type: ALS + - als: + backendRefs: + - name: envoy-als + namespace: monitoring + port: 9000 + type: TCP + type: ALS + - openTelemetry: + host: otel-collector.monitoring.svc.cluster.local + port: 4317 + resources: + k8s.cluster.name: cluster-1 + type: OpenTelemetry + status: {} + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + als: + - destination: + name: accesslog_als_2_1 + settings: + - addressType: IP + endpoints: + - host: 10.240.0.10 + port: 9090 + protocol: GRPC + http: + requestHeaders: + - x-client-ip-address + responseHeaders: + - cache-control + responseTrailers: + - expires + logType: Route + name: accesslog + text: | + this is a route log + type: HTTP + - destination: + name: accesslog_als_2_2 + settings: + - addressType: IP + endpoints: + - host: 10.240.0.10 + port: 9090 + protocol: GRPC + logType: Route + name: envoy-gateway-system/test + text: | + this is a route log + type: TCP + - destination: + name: accesslog_als_3_1 + settings: + - addressType: IP + endpoints: + - host: 10.240.0.10 + port: 9090 + protocol: GRPC + http: + requestHeaders: + - x-client-ip-address + responseHeaders: + - cache-control + responseTrailers: + - expires + logType: Listener + name: accesslog + text: | + this is a listener log + type: HTTP + - destination: + name: accesslog_als_3_2 + settings: + - addressType: IP + endpoints: + - host: 10.240.0.10 + port: 9090 + protocol: GRPC + logType: Listener + name: envoy-gateway-system/test + text: | + this is a listener log + type: TCP + - destination: + name: accesslog_als_4_1 + settings: + - addressType: IP + endpoints: + - host: 10.240.0.10 + port: 9090 + protocol: GRPC + http: + requestHeaders: + - x-client-ip-address + responseHeaders: + - cache-control + responseTrailers: + - expires + name: accesslog + text: | + this is a Global log + type: HTTP + - destination: + name: accesslog_als_4_2 + settings: + - addressType: IP + endpoints: + - host: 10.240.0.10 + port: 9090 + protocol: GRPC + name: envoy-gateway-system/test + text: | + this is a Global log + type: TCP + openTelemetry: + - authority: otel-collector.monitoring.svc.cluster.local + destination: + name: accesslog_otel_2_3 + settings: + - endpoints: + - host: otel-collector.monitoring.svc.cluster.local + port: 4317 + protocol: GRPC + weight: 1 + logType: Route + resources: + k8s.cluster.name: cluster-1 + text: | + this is a route log + - authority: otel-collector.monitoring.svc.cluster.local + destination: + name: accesslog_otel_3_3 + settings: + - endpoints: + - host: otel-collector.monitoring.svc.cluster.local + port: 4317 + protocol: GRPC + weight: 1 + logType: Listener + resources: + k8s.cluster.name: cluster-1 + text: | + this is a listener log + - authority: otel-collector.monitoring.svc.cluster.local + destination: + name: accesslog_otel_4_3 + settings: + - endpoints: + - host: otel-collector.monitoring.svc.cluster.local + port: 4317 + protocol: GRPC + weight: 1 + resources: + k8s.cluster.name: cluster-1 + text: | + this is a Global log + text: + - logType: Route + path: /dev/stdout + - logType: Listener + path: /dev/stdout + - format: | + this is a route log + logType: Route + path: /dev/stdout + - format: | + this is a listener log + logType: Listener + path: /dev/stdout + - format: | + this is a Global log + path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/envoy-gateway/httproute-1/rule/0 + settings: + - weight: 1 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: envoy-gateway + name: httproute/envoy-gateway/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + exact: /exact + name: "" diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 40e7da917f3..53eb34fa2a6 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -1739,6 +1739,13 @@ type RateLimitValue struct { Unit RateLimitUnit `json:"unit" yaml:"unit"` } +type ProxyAccessLogType egv1a1.ProxyAccessLogType + +const ( + ProxyAccessLogTypeRoute = ProxyAccessLogType(egv1a1.ProxyAccessLogTypeRoute) + ProxyAccessLogTypeListener = ProxyAccessLogType(egv1a1.ProxyAccessLogTypeListener) +) + // AccessLog holds the access logging configuration. // +k8s:deepcopy-gen=true type AccessLog struct { @@ -1751,17 +1758,19 @@ type AccessLog struct { // TextAccessLog holds the configuration for text access logging. // +k8s:deepcopy-gen=true type TextAccessLog struct { - CELMatches []string `json:"celMatches,omitempty" yaml:"celMatches,omitempty"` - Format *string `json:"format,omitempty" yaml:"format,omitempty"` - Path string `json:"path" yaml:"path"` + CELMatches []string `json:"celMatches,omitempty" yaml:"celMatches,omitempty"` + Format *string `json:"format,omitempty" yaml:"format,omitempty"` + Path string `json:"path" yaml:"path"` + LogType *ProxyAccessLogType `json:"logType,omitempty" yaml:"logType,omitempty"` } // JSONAccessLog holds the configuration for JSON access logging. // +k8s:deepcopy-gen=true type JSONAccessLog struct { - CELMatches []string `json:"celMatches,omitempty" yaml:"celMatches,omitempty"` - JSON map[string]string `json:"json,omitempty" yaml:"json,omitempty"` - Path string `json:"path" yaml:"path"` + CELMatches []string `json:"celMatches,omitempty" yaml:"celMatches,omitempty"` + JSON map[string]string `json:"json,omitempty" yaml:"json,omitempty"` + Path string `json:"path" yaml:"path"` + LogType *ProxyAccessLogType `json:"logType,omitempty" yaml:"logType,omitempty"` } // ALSAccessLog holds the configuration for gRPC ALS access logging. @@ -1775,6 +1784,7 @@ type ALSAccessLog struct { Text *string `json:"text,omitempty" yaml:"text,omitempty"` Attributes map[string]string `json:"attributes,omitempty" yaml:"attributes,omitempty"` HTTP *ALSAccessLogHTTP `json:"http,omitempty" yaml:"http,omitempty"` + LogType *ProxyAccessLogType `json:"logType,omitempty" yaml:"logType,omitempty"` } // ALSAccessLogHTTP holds the configuration for HTTP ALS access logging. @@ -1788,13 +1798,14 @@ type ALSAccessLogHTTP struct { // OpenTelemetryAccessLog holds the configuration for OpenTelemetry access logging. // +k8s:deepcopy-gen=true type OpenTelemetryAccessLog struct { - CELMatches []string `json:"celMatches,omitempty" yaml:"celMatches,omitempty"` - Authority string `json:"authority,omitempty" yaml:"authority,omitempty"` - Text *string `json:"text,omitempty" yaml:"text,omitempty"` - Attributes map[string]string `json:"attributes,omitempty" yaml:"attributes,omitempty"` - Resources map[string]string `json:"resources,omitempty" yaml:"resources,omitempty"` - Destination RouteDestination `json:"destination,omitempty" yaml:"destination,omitempty"` - Traffic *TrafficFeatures `json:"traffic,omitempty" yaml:"traffic,omitempty"` + CELMatches []string `json:"celMatches,omitempty" yaml:"celMatches,omitempty"` + Authority string `json:"authority,omitempty" yaml:"authority,omitempty"` + Text *string `json:"text,omitempty" yaml:"text,omitempty"` + Attributes map[string]string `json:"attributes,omitempty" yaml:"attributes,omitempty"` + Resources map[string]string `json:"resources,omitempty" yaml:"resources,omitempty"` + Destination RouteDestination `json:"destination,omitempty" yaml:"destination,omitempty"` + Traffic *TrafficFeatures `json:"traffic,omitempty" yaml:"traffic,omitempty"` + LogType *ProxyAccessLogType `json:"logType,omitempty" yaml:"logType,omitempty"` } // EnvoyPatchPolicy defines the intermediate representation of the EnvoyPatchPolicy resource. diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 4400b555dd7..1a0185bbb9f 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -47,6 +47,11 @@ func (in *ALSAccessLog) DeepCopyInto(out *ALSAccessLog) { *out = new(ALSAccessLogHTTP) (*in).DeepCopyInto(*out) } + if in.LogType != nil { + in, out := &in.LogType, &out.LogType + *out = new(ProxyAccessLogType) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ALSAccessLog. @@ -1690,6 +1695,11 @@ func (in *JSONAccessLog) DeepCopyInto(out *JSONAccessLog) { (*out)[key] = val } } + if in.LogType != nil { + in, out := &in.LogType, &out.LogType + *out = new(ProxyAccessLogType) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JSONAccessLog. @@ -1976,6 +1986,11 @@ func (in *OpenTelemetryAccessLog) DeepCopyInto(out *OpenTelemetryAccessLog) { *out = new(TrafficFeatures) (*in).DeepCopyInto(*out) } + if in.LogType != nil { + in, out := &in.LogType, &out.LogType + *out = new(ProxyAccessLogType) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OpenTelemetryAccessLog. @@ -2985,6 +3000,11 @@ func (in *TextAccessLog) DeepCopyInto(out *TextAccessLog) { *out = new(string) **out = **in } + if in.LogType != nil { + in, out := &in.LogType, &out.LogType + *out = new(ProxyAccessLogType) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TextAccessLog. diff --git a/internal/xds/translator/accesslog.go b/internal/xds/translator/accesslog.go index 8acb6e4b005..6660ba8fab6 100644 --- a/internal/xds/translator/accesslog.go +++ b/internal/xds/translator/accesslog.go @@ -90,15 +90,24 @@ var ( } ) -func buildXdsAccessLog(al *ir.AccessLog, forListener bool) []*accesslog.AccessLog { +func buildXdsAccessLog(al *ir.AccessLog, accessLogType ir.ProxyAccessLogType) []*accesslog.AccessLog { if al == nil { return nil } totalLen := len(al.Text) + len(al.JSON) + len(al.OpenTelemetry) accessLogs := make([]*accesslog.AccessLog, 0, totalLen) + // handle text file access logs for _, text := range al.Text { + // Filter out logs that are not Global or match the desired access log type + if !(text.LogType == nil || *text.LogType == accessLogType) { + continue + } + + // NR is only added to listener logs originating from a global log configuration + defaultLogTypeForListener := accessLogType == ir.ProxyAccessLogTypeListener && text.LogType == nil + filelog := &fileaccesslog.FileAccessLog{ Path: text.Path, } @@ -131,11 +140,19 @@ func buildXdsAccessLog(al *ir.AccessLog, forListener bool) []*accesslog.AccessLo ConfigType: &accesslog.AccessLog_TypedConfig{ TypedConfig: accesslogAny, }, - Filter: buildAccessLogFilter(text.CELMatches, forListener), + Filter: buildAccessLogFilter(text.CELMatches, defaultLogTypeForListener), }) } // handle json file access logs for _, json := range al.JSON { + // Filter out logs that are not Global or match the desired access log type + if !(json.LogType == nil || *json.LogType == accessLogType) { + continue + } + + // NR is only added to listener logs originating from a global log configuration + defaultLogTypeForListener := accessLogType == ir.ProxyAccessLogTypeListener && json.LogType == nil + jsonFormat := &structpb.Struct{ Fields: make(map[string]*structpb.Value, len(json.JSON)), } @@ -174,11 +191,19 @@ func buildXdsAccessLog(al *ir.AccessLog, forListener bool) []*accesslog.AccessLo ConfigType: &accesslog.AccessLog_TypedConfig{ TypedConfig: accesslogAny, }, - Filter: buildAccessLogFilter(json.CELMatches, forListener), + Filter: buildAccessLogFilter(json.CELMatches, defaultLogTypeForListener), }) } // handle ALS access logs for _, als := range al.ALS { + // Filter out logs that are not Global or match the desired access log type + if !(als.LogType == nil || *als.LogType == accessLogType) { + continue + } + + // NR is only added to listener logs originating from a global log configuration + defaultLogTypeForListener := accessLogType == ir.ProxyAccessLogTypeListener && als.LogType == nil + cc := &grpcaccesslog.CommonGrpcAccessLogConfig{ LogName: als.LogName, GrpcService: &cfgcore.GrpcService{ @@ -209,7 +234,7 @@ func buildXdsAccessLog(al *ir.AccessLog, forListener bool) []*accesslog.AccessLo ConfigType: &accesslog.AccessLog_TypedConfig{ TypedConfig: accesslogAny, }, - Filter: buildAccessLogFilter(als.CELMatches, forListener), + Filter: buildAccessLogFilter(als.CELMatches, defaultLogTypeForListener), }) case egv1a1.ALSEnvoyProxyAccessLogTypeTCP: alCfg := &grpcaccesslog.TcpGrpcAccessLogConfig{ @@ -222,12 +247,20 @@ func buildXdsAccessLog(al *ir.AccessLog, forListener bool) []*accesslog.AccessLo ConfigType: &accesslog.AccessLog_TypedConfig{ TypedConfig: accesslogAny, }, - Filter: buildAccessLogFilter(als.CELMatches, forListener), + Filter: buildAccessLogFilter(als.CELMatches, defaultLogTypeForListener), }) } } // handle open telemetry access logs for _, otel := range al.OpenTelemetry { + // Filter out logs that are not Global or match the desired access log type + if !(otel.LogType == nil || *otel.LogType == accessLogType) { + continue + } + + // NR is only added to listener logs originating from a global log configuration + defaultLogTypeForListener := accessLogType == ir.ProxyAccessLogTypeListener && otel.LogType == nil + al := &otelaccesslog.OpenTelemetryAccessLogConfig{ CommonConfig: &grpcaccesslog.CommonGrpcAccessLogConfig{ LogName: otelLogName, @@ -270,7 +303,7 @@ func buildXdsAccessLog(al *ir.AccessLog, forListener bool) []*accesslog.AccessLo ConfigType: &accesslog.AccessLog_TypedConfig{ TypedConfig: accesslogAny, }, - Filter: buildAccessLogFilter(otel.CELMatches, forListener), + Filter: buildAccessLogFilter(otel.CELMatches, defaultLogTypeForListener), }) } @@ -292,13 +325,13 @@ func celAccessLogFilter(expr string) *accesslog.AccessLogFilter { } } -func buildAccessLogFilter(exprs []string, forListener bool) *accesslog.AccessLogFilter { +func buildAccessLogFilter(exprs []string, withNoRouteMatchFilter bool) *accesslog.AccessLogFilter { // add filter for access logs var filters []*accesslog.AccessLogFilter for _, expr := range exprs { filters = append(filters, celAccessLogFilter(expr)) } - if forListener { + if withNoRouteMatchFilter { filters = append(filters, listenerAccessLogFilter) } diff --git a/internal/xds/translator/listener.go b/internal/xds/translator/listener.go index 98f7c28e372..9cc8e61f6ed 100644 --- a/internal/xds/translator/listener.go +++ b/internal/xds/translator/listener.go @@ -151,7 +151,7 @@ func originalIPDetectionExtensions(clientIPDetection *ir.ClientIPDetectionSettin // TODO: Improve function parameters func buildXdsTCPListener(name, address string, port uint32, keepalive *ir.TCPKeepalive, connection *ir.ClientConnection, accesslog *ir.AccessLog) *listenerv3.Listener { socketOptions := buildTCPSocketOptions(keepalive) - al := buildXdsAccessLog(accesslog, true) + al := buildXdsAccessLog(accesslog, ir.ProxyAccessLogTypeListener) bufferLimitBytes := buildPerConnectionBufferLimitBytes(connection) return &listenerv3.Listener{ Name: name, @@ -183,7 +183,7 @@ func buildPerConnectionBufferLimitBytes(connection *ir.ClientConnection) *wrappe func buildXdsQuicListener(name, address string, port uint32, accesslog *ir.AccessLog) *listenerv3.Listener { xdsListener := &listenerv3.Listener{ Name: name + "-quic", - AccessLog: buildXdsAccessLog(accesslog, true), + AccessLog: buildXdsAccessLog(accesslog, ir.ProxyAccessLogTypeListener), Address: &corev3.Address{ Address: &corev3.Address_SocketAddress{ SocketAddress: &corev3.SocketAddress{ @@ -220,7 +220,7 @@ func buildXdsQuicListener(name, address string, port uint32, accesslog *ir.Acces func (t *Translator) addHCMToXDSListener(xdsListener *listenerv3.Listener, irListener *ir.HTTPListener, accesslog *ir.AccessLog, tracing *ir.Tracing, http3Listener bool, connection *ir.ClientConnection, ) error { - al := buildXdsAccessLog(accesslog, false) + al := buildXdsAccessLog(accesslog, ir.ProxyAccessLogTypeRoute) hcmTracing, err := buildHCMTracing(tracing) if err != nil { @@ -494,7 +494,7 @@ func addXdsTCPFilterChain(xdsListener *listenerv3.Listener, irRoute *ir.TCPRoute statPrefix = strings.Join([]string{statPrefix, strconv.Itoa(int(xdsListener.Address.GetSocketAddress().GetPortValue()))}, "-") mgr := &tcpv3.TcpProxy{ - AccessLog: buildXdsAccessLog(accesslog, false), + AccessLog: buildXdsAccessLog(accesslog, ir.ProxyAccessLogTypeRoute), StatPrefix: statPrefix, ClusterSpecifier: &tcpv3.TcpProxy_Cluster{ Cluster: clusterName, @@ -773,7 +773,7 @@ func buildXdsUDPListener(clusterName string, udpListener *ir.UDPListener, access udpProxy := &udpv3.UdpProxyConfig{ StatPrefix: statPrefix, - AccessLog: buildXdsAccessLog(accesslog, false), + AccessLog: buildXdsAccessLog(accesslog, ir.ProxyAccessLogTypeRoute), RouteSpecifier: &udpv3.UdpProxyConfig_Matcher{ Matcher: &matcher.Matcher{ OnNoMatch: &matcher.Matcher_OnMatch{ @@ -794,7 +794,7 @@ func buildXdsUDPListener(clusterName string, udpListener *ir.UDPListener, access xdsListener := &listenerv3.Listener{ Name: udpListener.Name, - AccessLog: buildXdsAccessLog(accesslog, true), + AccessLog: buildXdsAccessLog(accesslog, ir.ProxyAccessLogTypeListener), Address: &corev3.Address{ Address: &corev3.Address_SocketAddress{ SocketAddress: &corev3.SocketAddress{ diff --git a/internal/xds/translator/testdata/in/xds-ir/accesslog-types.yaml b/internal/xds/translator/testdata/in/xds-ir/accesslog-types.yaml new file mode 100644 index 00000000000..d2458abfce9 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/accesslog-types.yaml @@ -0,0 +1,184 @@ +accessLog: + als: + - destination: + name: accesslog_als_0_1 + settings: + - addressType: IP + endpoints: + - host: 10.240.0.10 + port: 9090 + protocol: GRPC + http: + requestHeaders: + - x-client-ip-address + responseHeaders: + - cache-control + responseTrailers: + - expires + logType: Route + name: accesslog + text: | + this is a route log + type: HTTP + - destination: + name: accesslog_als_0_2 + settings: + - addressType: IP + endpoints: + - host: 10.240.0.10 + port: 9090 + protocol: GRPC + logType: Route + name: envoy-gateway-system/test + text: | + this is a route log + type: TCP + - destination: + name: accesslog_als_1_1 + settings: + - addressType: IP + endpoints: + - host: 10.240.0.10 + port: 9090 + protocol: GRPC + http: + requestHeaders: + - x-client-ip-address + responseHeaders: + - cache-control + responseTrailers: + - expires + logType: Listener + name: accesslog + text: | + this is a listener log + type: HTTP + - destination: + name: accesslog_als_1_2 + settings: + - addressType: IP + endpoints: + - host: 10.240.0.10 + port: 9090 + protocol: GRPC + logType: Listener + name: envoy-gateway-system/test + text: | + this is a listener log + type: TCP + - destination: + name: accesslog_als_2_1 + settings: + - addressType: IP + endpoints: + - host: 10.240.0.10 + port: 9090 + protocol: GRPC + http: + requestHeaders: + - x-client-ip-address + responseHeaders: + - cache-control + responseTrailers: + - expires + name: accesslog + text: | + this is a Global log + type: HTTP + - destination: + name: accesslog_als_2_2 + settings: + - addressType: IP + endpoints: + - host: 10.240.0.10 + port: 9090 + protocol: GRPC + name: envoy-gateway-system/test + text: | + this is a Global log + type: TCP + openTelemetry: + - authority: otel-collector.monitoring.svc.cluster.local + destination: + name: accesslog_otel_0_3 + settings: + - endpoints: + - host: otel-collector.monitoring.svc.cluster.local + port: 4317 + protocol: GRPC + weight: 1 + logType: Route + resources: + k8s.cluster.name: cluster-1 + text: | + this is a route log + - authority: otel-collector.monitoring.svc.cluster.local + destination: + name: accesslog_otel_1_3 + settings: + - endpoints: + - host: otel-collector.monitoring.svc.cluster.local + port: 4317 + protocol: GRPC + weight: 1 + logType: Listener + resources: + k8s.cluster.name: cluster-1 + text: | + this is a listener log + - authority: otel-collector.monitoring.svc.cluster.local + destination: + name: accesslog_otel_2_3 + settings: + - endpoints: + - host: otel-collector.monitoring.svc.cluster.local + port: 4317 + protocol: GRPC + weight: 1 + resources: + k8s.cluster.name: cluster-1 + text: | + this is a Global log + text: + - logType: Route + path: /dev/stdout + - logType: Listener + path: /dev/stdout + - format: | + this is a route log + logType: Route + path: /dev/stdout + - format: | + this is a listener log + logType: Listener + path: /dev/stdout + - format: | + this is a Global log + path: /dev/stdout +http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - name: "direct-route" + hostname: "*" + destination: + name: "direct-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + directResponse: + body: "Unknown custom filter type: UnsupportedType" + statusCode: 500 diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-types.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-types.clusters.yaml new file mode 100644 index 00000000000..e0328b6e26c --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-types.clusters.yaml @@ -0,0 +1,263 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: direct-route-dest + lbPolicy: LEAST_REQUEST + name: direct-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: accesslog_als_0_1 + lbPolicy: LEAST_REQUEST + name: accesslog_als_0_1 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: accesslog_als_0_2 + lbPolicy: LEAST_REQUEST + name: accesslog_als_0_2 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: accesslog_als_1_1 + lbPolicy: LEAST_REQUEST + name: accesslog_als_1_1 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: accesslog_als_1_2 + lbPolicy: LEAST_REQUEST + name: accesslog_als_1_2 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: accesslog_als_2_1 + lbPolicy: LEAST_REQUEST + name: accesslog_als_2_1 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: accesslog_als_2_2 + lbPolicy: LEAST_REQUEST + name: accesslog_als_2_2 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: accesslog_otel_0_3 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: otel-collector.monitoring.svc.cluster.local + portValue: 4317 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: accesslog_otel_0_3/backend/0 + name: accesslog_otel_0_3 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: accesslog_otel_1_3 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: otel-collector.monitoring.svc.cluster.local + portValue: 4317 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: accesslog_otel_1_3/backend/0 + name: accesslog_otel_1_3 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: accesslog_otel_2_3 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: otel-collector.monitoring.svc.cluster.local + portValue: 4317 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: accesslog_otel_2_3/backend/0 + name: accesslog_otel_2_3 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-types.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-types.endpoints.yaml new file mode 100644 index 00000000000..e9526ab5d90 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-types.endpoints.yaml @@ -0,0 +1,84 @@ +- clusterName: direct-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: direct-route-dest/backend/0 +- clusterName: accesslog_als_0_1 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.240.0.10 + portValue: 9090 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: accesslog_als_0_1/backend/0 +- clusterName: accesslog_als_0_2 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.240.0.10 + portValue: 9090 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: accesslog_als_0_2/backend/0 +- clusterName: accesslog_als_1_1 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.240.0.10 + portValue: 9090 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: accesslog_als_1_1/backend/0 +- clusterName: accesslog_als_1_2 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.240.0.10 + portValue: 9090 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: accesslog_als_1_2/backend/0 +- clusterName: accesslog_als_2_1 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.240.0.10 + portValue: 9090 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: accesslog_als_2_1/backend/0 +- clusterName: accesslog_als_2_2 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 10.240.0.10 + portValue: 9090 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: accesslog_als_2_2/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-types.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-types.listeners.yaml new file mode 100644 index 00000000000..dbb30726378 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-types.listeners.yaml @@ -0,0 +1,300 @@ +- accessLog: + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} + path: /dev/stdout + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + this is a listener log + path: /dev/stdout + - filter: + responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + this is a Global log + path: /dev/stdout + - name: envoy.access_loggers.http_grpc + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.HttpGrpcAccessLogConfig + additionalRequestHeadersToLog: + - x-client-ip-address + additionalResponseHeadersToLog: + - cache-control + additionalResponseTrailersToLog: + - expires + commonConfig: + grpcService: + envoyGrpc: + clusterName: accesslog_als_1_1 + logName: accesslog + transportApiVersion: V3 + - name: envoy.access_loggers.tcp_grpc + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.TcpGrpcAccessLogConfig + commonConfig: + grpcService: + envoyGrpc: + clusterName: accesslog_als_1_2 + logName: envoy-gateway-system/test + transportApiVersion: V3 + - filter: + responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.http_grpc + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.HttpGrpcAccessLogConfig + additionalRequestHeadersToLog: + - x-client-ip-address + additionalResponseHeadersToLog: + - cache-control + additionalResponseTrailersToLog: + - expires + commonConfig: + grpcService: + envoyGrpc: + clusterName: accesslog_als_2_1 + logName: accesslog + transportApiVersion: V3 + - filter: + responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.tcp_grpc + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.TcpGrpcAccessLogConfig + commonConfig: + grpcService: + envoyGrpc: + clusterName: accesslog_als_2_2 + logName: envoy-gateway-system/test + transportApiVersion: V3 + - name: envoy.access_loggers.open_telemetry + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig + attributes: + values: + - key: k8s.namespace.name + value: + stringValue: '%ENVIRONMENT(ENVOY_GATEWAY_NAMESPACE)%' + - key: k8s.pod.name + value: + stringValue: '%ENVIRONMENT(ENVOY_POD_NAME)%' + body: + stringValue: | + this is a listener log + commonConfig: + grpcService: + envoyGrpc: + authority: otel-collector.monitoring.svc.cluster.local + clusterName: accesslog_otel_1_3 + logName: otel_envoy_accesslog + transportApiVersion: V3 + resourceAttributes: + values: + - key: k8s.cluster.name + value: + stringValue: cluster-1 + - filter: + responseFlagFilter: + flags: + - NR + name: envoy.access_loggers.open_telemetry + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig + attributes: + values: + - key: k8s.namespace.name + value: + stringValue: '%ENVIRONMENT(ENVOY_GATEWAY_NAMESPACE)%' + - key: k8s.pod.name + value: + stringValue: '%ENVIRONMENT(ENVOY_POD_NAME)%' + body: + stringValue: | + this is a Global log + commonConfig: + grpcService: + envoyGrpc: + authority: otel-collector.monitoring.svc.cluster.local + clusterName: accesslog_otel_2_3 + logName: otel_envoy_accesslog + transportApiVersion: V3 + resourceAttributes: + values: + - key: k8s.cluster.name + value: + stringValue: cluster-1 + address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + accessLog: + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + {"start_time":"%START_TIME%","method":"%REQ(:METHOD)%","x-envoy-origin-path":"%REQ(X-ENVOY-ORIGINAL-PATH?:PATH)%","protocol":"%PROTOCOL%","response_code":"%RESPONSE_CODE%","response_flags":"%RESPONSE_FLAGS%","response_code_details":"%RESPONSE_CODE_DETAILS%","connection_termination_details":"%CONNECTION_TERMINATION_DETAILS%","upstream_transport_failure_reason":"%UPSTREAM_TRANSPORT_FAILURE_REASON%","bytes_received":"%BYTES_RECEIVED%","bytes_sent":"%BYTES_SENT%","duration":"%DURATION%","x-envoy-upstream-service-time":"%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)%","x-forwarded-for":"%REQ(X-FORWARDED-FOR)%","user-agent":"%REQ(USER-AGENT)%","x-request-id":"%REQ(X-REQUEST-ID)%",":authority":"%REQ(:AUTHORITY)%","upstream_host":"%UPSTREAM_HOST%","upstream_cluster":"%UPSTREAM_CLUSTER%","upstream_local_address":"%UPSTREAM_LOCAL_ADDRESS%","downstream_local_address":"%DOWNSTREAM_LOCAL_ADDRESS%","downstream_remote_address":"%DOWNSTREAM_REMOTE_ADDRESS%","requested_server_name":"%REQUESTED_SERVER_NAME%","route_name":"%ROUTE_NAME%"} + path: /dev/stdout + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + this is a route log + path: /dev/stdout + - name: envoy.access_loggers.file + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + logFormat: + textFormatSource: + inlineString: | + this is a Global log + path: /dev/stdout + - name: envoy.access_loggers.http_grpc + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.HttpGrpcAccessLogConfig + additionalRequestHeadersToLog: + - x-client-ip-address + additionalResponseHeadersToLog: + - cache-control + additionalResponseTrailersToLog: + - expires + commonConfig: + grpcService: + envoyGrpc: + clusterName: accesslog_als_0_1 + logName: accesslog + transportApiVersion: V3 + - name: envoy.access_loggers.tcp_grpc + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.TcpGrpcAccessLogConfig + commonConfig: + grpcService: + envoyGrpc: + clusterName: accesslog_als_0_2 + logName: envoy-gateway-system/test + transportApiVersion: V3 + - name: envoy.access_loggers.http_grpc + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.HttpGrpcAccessLogConfig + additionalRequestHeadersToLog: + - x-client-ip-address + additionalResponseHeadersToLog: + - cache-control + additionalResponseTrailersToLog: + - expires + commonConfig: + grpcService: + envoyGrpc: + clusterName: accesslog_als_2_1 + logName: accesslog + transportApiVersion: V3 + - name: envoy.access_loggers.tcp_grpc + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.grpc.v3.TcpGrpcAccessLogConfig + commonConfig: + grpcService: + envoyGrpc: + clusterName: accesslog_als_2_2 + logName: envoy-gateway-system/test + transportApiVersion: V3 + - name: envoy.access_loggers.open_telemetry + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig + attributes: + values: + - key: k8s.namespace.name + value: + stringValue: '%ENVIRONMENT(ENVOY_GATEWAY_NAMESPACE)%' + - key: k8s.pod.name + value: + stringValue: '%ENVIRONMENT(ENVOY_POD_NAME)%' + body: + stringValue: | + this is a route log + commonConfig: + grpcService: + envoyGrpc: + authority: otel-collector.monitoring.svc.cluster.local + clusterName: accesslog_otel_0_3 + logName: otel_envoy_accesslog + transportApiVersion: V3 + resourceAttributes: + values: + - key: k8s.cluster.name + value: + stringValue: cluster-1 + - name: envoy.access_loggers.open_telemetry + typedConfig: + '@type': type.googleapis.com/envoy.extensions.access_loggers.open_telemetry.v3.OpenTelemetryAccessLogConfig + attributes: + values: + - key: k8s.namespace.name + value: + stringValue: '%ENVIRONMENT(ENVOY_GATEWAY_NAMESPACE)%' + - key: k8s.pod.name + value: + stringValue: '%ENVIRONMENT(ENVOY_POD_NAME)%' + body: + stringValue: | + this is a Global log + commonConfig: + grpcService: + envoyGrpc: + authority: otel-collector.monitoring.svc.cluster.local + clusterName: accesslog_otel_2_3 + logName: otel_envoy_accesslog + transportApiVersion: V3 + resourceAttributes: + values: + - key: k8s.cluster.name + value: + stringValue: cluster-1 + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: envoy-gateway/gateway-1/http + serverHeaderTransformation: PASS_THROUGH + statPrefix: http-10080 + useRemoteAddress: true + name: envoy-gateway/gateway-1/http + name: envoy-gateway/gateway-1/http + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/accesslog-types.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/accesslog-types.routes.yaml new file mode 100644 index 00000000000..ff2210f8d50 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/accesslog-types.routes.yaml @@ -0,0 +1,20 @@ +- ignorePortInHostMatching: true + name: envoy-gateway/gateway-1/http + virtualHosts: + - domains: + - '*' + metadata: + filterMetadata: + envoy-gateway: + resources: + - kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http/* + routes: + - directResponse: + status: 500 + match: + prefix: / + name: direct-route diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index bc01189920d..c26a8c713f8 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -2995,6 +2995,7 @@ _Appears in:_ | `format` | _[ProxyAccessLogFormat](#proxyaccesslogformat)_ | false | Format defines the format of accesslog.
This will be ignored if sink type is ALS. | | `matches` | _string array_ | true | Matches defines the match conditions for accesslog in CEL expression.
An accesslog will be emitted only when one or more match conditions are evaluated to true.
Invalid [CEL](https://www.envoyproxy.io/docs/envoy/latest/xds/type/v3/cel.proto.html#common-expression-language-cel-proto) expressions will be ignored. | | `sinks` | _[ProxyAccessLogSink](#proxyaccesslogsink) array_ | true | Sinks defines the sinks of accesslog. | +| `type` | _[ProxyAccessLogType](#proxyaccesslogtype)_ | false | Type defines the component emitting the accesslog, such as Listener and Route.
If type not defined, the setting would apply to:
(1) All Routes.
(2) Listeners if and only if Envoy does not find a matching route for a request.
If type is defined, the accesslog settings would apply to the relevant component (as-is). | #### ProxyAccessLogSink diff --git a/site/content/en/latest/tasks/observability/proxy-accesslog.md b/site/content/en/latest/tasks/observability/proxy-accesslog.md index fb0200f1739..17d444b8636 100644 --- a/site/content/en/latest/tasks/observability/proxy-accesslog.md +++ b/site/content/en/latest/tasks/observability/proxy-accesslog.md @@ -249,3 +249,62 @@ Envoy Gateway provides additional metadata about the K8s resources that were tra For example, details about the `HTTPRoute` and `GRPCRoute` (kind, group, name, namespace and annotations) are available for access log formatter using the `METADATA` operator. To enrich logs, users can add log operator such as: `%METADATA(ROUTE:envoy-gateway:resources)%` to their access log format. + +## Access Log Types + +By default, Access Log settings would apply to: +- All Routes +- If traffic is not matched by any Route known to Envoy, the Listener would emit the access log instead + +Users may wish to customize this behavior: +- Emit Access Logs by all Listeners for all traffic with specific settings +- Do not emit Route-oriented access logs when a route is not matched. + +To achieve this, users can select if Access Log settings follow the default behavior or apply specifically to +Routes or Listeners by specifying the setting's type. + +**Note**: When users define their own Access Log settings (with or without a type), the default Envoy Gateway +file access log is no longer configured. It can be re-enabled explicitly by adding empty settings for the desired components. + +In the following example: +- Route Access logs would use the default Envoy Gateway format and sink +- Listener Access logs are customized to report transport-level failures and connection attributes + +```shell +kubectl apply -f - <This will be ignored if sink type is ALS. | | `matches` | _string array_ | true | Matches defines the match conditions for accesslog in CEL expression.
An accesslog will be emitted only when one or more match conditions are evaluated to true.
Invalid [CEL](https://www.envoyproxy.io/docs/envoy/latest/xds/type/v3/cel.proto.html#common-expression-language-cel-proto) expressions will be ignored. | | `sinks` | _[ProxyAccessLogSink](#proxyaccesslogsink) array_ | true | Sinks defines the sinks of accesslog. | +| `type` | _[ProxyAccessLogType](#proxyaccesslogtype)_ | false | Type defines the component emitting the accesslog, such as Listener and Route.
If type not defined, the setting would apply to:
(1) All Routes.
(2) Listeners if and only if Envoy does not find a matching route for a request.
If type is defined, the accesslog settings would apply to the relevant component (as-is). | #### ProxyAccessLogSink diff --git a/test/config/gatewayclass.yaml b/test/config/gatewayclass.yaml index fa07a159305..6e8acf3d0f8 100644 --- a/test/config/gatewayclass.yaml +++ b/test/config/gatewayclass.yaml @@ -68,6 +68,15 @@ spec: namespace: monitoring port: 8080 type: HTTP + - type: Listener + format: + type: Text + text: | + LISTENER ACCESS LOG %UPSTREAM_PROTOCOL% %RESPONSE_CODE% + sinks: + - type: File + file: + path: /dev/stdout tracing: provider: backendRefs: diff --git a/test/e2e/tests/accesslog.go b/test/e2e/tests/accesslog.go index 2019d92568c..b2c9a28ac94 100644 --- a/test/e2e/tests/accesslog.go +++ b/test/e2e/tests/accesslog.go @@ -81,6 +81,38 @@ var FileAccessLogTest = suite.ConformanceTest{ runLogTest(t, suite, gwAddr, expectedResponse, labels, match, 0) }) + + t.Run("Listener Logs", func(t *testing.T) { + // Ensure that Listener is emitting the log: protocol and response code should be + // empty in listener logs as they are upstream L7 attributes + expectedMatch := "LISTENER ACCESS LOG - 0" + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "accesslog-file", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + expectedResponse := httputils.ExpectedResponse{ + Request: httputils.Request{ + Path: "/file", + Headers: map[string]string{ + "connection": "close", + }, + }, + ExpectedRequest: &httputils.ExpectedRequest{ + Request: httputils.Request{ + Path: "/file", + }, + }, + Response: httputils.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + // make sure listener is ready + httputils.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse) + + runLogTest(t, suite, gwAddr, expectedResponse, labels, expectedMatch, 0) + }) }, } From f1df0c95e0cffaec3f119d7dfb63607a78a4f808 Mon Sep 17 00:00:00 2001 From: shaoyue Date: Fri, 11 Oct 2024 00:19:59 +0800 Subject: [PATCH 07/56] fix dashboard typos (#4422) Signed-off-by: haorenfsa --- charts/gateway-addons-helm/dashboards/envoy-clusters.json | 4 ++-- .../gateway-addons-helm/dashboards/envoy-proxy-global.json | 2 +- test/helm/gateway-addons-helm/default.out.yaml | 6 +++--- test/helm/gateway-addons-helm/e2e.out.yaml | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/charts/gateway-addons-helm/dashboards/envoy-clusters.json b/charts/gateway-addons-helm/dashboards/envoy-clusters.json index 4505f188e7d..8ee91675d96 100644 --- a/charts/gateway-addons-helm/dashboards/envoy-clusters.json +++ b/charts/gateway-addons-helm/dashboards/envoy-clusters.json @@ -259,7 +259,7 @@ "uid": "$datasource" }, "editorMode": "builder", - "expr": "SUM(envoy_server_memory_allocated{})", + "expr": "sum(envoy_server_memory_allocated{})", "format": "time_series", "intervalFactor": 1, "legendFormat": "", @@ -339,7 +339,7 @@ "uid": "$datasource" }, "editorMode": "code", - "expr": "SUM(envoy_server_memory_heap_size)", + "expr": "sum(envoy_server_memory_heap_size)", "format": "time_series", "intervalFactor": 1, "legendFormat": "", diff --git a/charts/gateway-addons-helm/dashboards/envoy-proxy-global.json b/charts/gateway-addons-helm/dashboards/envoy-proxy-global.json index f2c0ae2b0d4..99522ae061e 100644 --- a/charts/gateway-addons-helm/dashboards/envoy-proxy-global.json +++ b/charts/gateway-addons-helm/dashboards/envoy-proxy-global.json @@ -1849,7 +1849,7 @@ "uid": "${datasource}" }, "editorMode": "builder", - "expr": "sum by(namespace) (rate(envoy_cluster_upstream_cx_rx_bytes_total{namespace=~\"$Namespace\"}[5m]))", + "expr": "sum by(namespace) (rate(envoy_cluster_upstream_cx_tx_bytes_total{namespace=~\"$Namespace\"}[5m]))", "instant": false, "legendFormat": "{{namespace}}", "range": true, diff --git a/test/helm/gateway-addons-helm/default.out.yaml b/test/helm/gateway-addons-helm/default.out.yaml index f2fac1dfe09..614a2d22454 100644 --- a/test/helm/gateway-addons-helm/default.out.yaml +++ b/test/helm/gateway-addons-helm/default.out.yaml @@ -941,7 +941,7 @@ data: "uid": "$datasource" }, "editorMode": "builder", - "expr": "SUM(envoy_server_memory_allocated{})", + "expr": "sum(envoy_server_memory_allocated{})", "format": "time_series", "intervalFactor": 1, "legendFormat": "", @@ -1021,7 +1021,7 @@ data: "uid": "$datasource" }, "editorMode": "code", - "expr": "SUM(envoy_server_memory_heap_size)", + "expr": "sum(envoy_server_memory_heap_size)", "format": "time_series", "intervalFactor": 1, "legendFormat": "", @@ -7247,7 +7247,7 @@ data: "uid": "${datasource}" }, "editorMode": "builder", - "expr": "sum by(namespace) (rate(envoy_cluster_upstream_cx_rx_bytes_total{namespace=~\"$Namespace\"}[5m]))", + "expr": "sum by(namespace) (rate(envoy_cluster_upstream_cx_tx_bytes_total{namespace=~\"$Namespace\"}[5m]))", "instant": false, "legendFormat": "{{namespace}}", "range": true, diff --git a/test/helm/gateway-addons-helm/e2e.out.yaml b/test/helm/gateway-addons-helm/e2e.out.yaml index 52ed8fcb97e..15445239f3d 100644 --- a/test/helm/gateway-addons-helm/e2e.out.yaml +++ b/test/helm/gateway-addons-helm/e2e.out.yaml @@ -973,7 +973,7 @@ data: "uid": "$datasource" }, "editorMode": "builder", - "expr": "SUM(envoy_server_memory_allocated{})", + "expr": "sum(envoy_server_memory_allocated{})", "format": "time_series", "intervalFactor": 1, "legendFormat": "", @@ -1053,7 +1053,7 @@ data: "uid": "$datasource" }, "editorMode": "code", - "expr": "SUM(envoy_server_memory_heap_size)", + "expr": "sum(envoy_server_memory_heap_size)", "format": "time_series", "intervalFactor": 1, "legendFormat": "", @@ -7279,7 +7279,7 @@ data: "uid": "${datasource}" }, "editorMode": "builder", - "expr": "sum by(namespace) (rate(envoy_cluster_upstream_cx_rx_bytes_total{namespace=~\"$Namespace\"}[5m]))", + "expr": "sum by(namespace) (rate(envoy_cluster_upstream_cx_tx_bytes_total{namespace=~\"$Namespace\"}[5m]))", "instant": false, "legendFormat": "{{namespace}}", "range": true, From c1f4837f4af7b47d19c829091eacc8e4d3340eb7 Mon Sep 17 00:00:00 2001 From: Isaac <10012479+jukie@users.noreply.github.com> Date: Sat, 12 Oct 2024 18:38:00 -0600 Subject: [PATCH 08/56] feat: add labels to envoyService config (#4427) --- api/v1alpha1/shared_types.go | 6 +++ api/v1alpha1/zz_generated.deepcopy.go | 7 ++++ .../gateway.envoyproxy.io_envoyproxies.yaml | 7 ++++ .../kubernetes/proxy/resource_provider.go | 22 ++++++++--- .../proxy/resource_provider_test.go | 28 ++++++++++++++ .../proxy/testdata/services/custom.yaml | 1 + .../testdata/services/override-labels.yaml | 37 +++++++++++++++++++ .../testdata/services/with-svc-labels.yaml | 32 ++++++++++++++++ site/content/en/latest/api/extension_types.md | 1 + site/content/zh/latest/api/extension_types.md | 1 + 10 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 internal/infrastructure/kubernetes/proxy/testdata/services/override-labels.yaml create mode 100644 internal/infrastructure/kubernetes/proxy/testdata/services/with-svc-labels.yaml diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go index 48e3471c77d..3f165cc6c2d 100644 --- a/api/v1alpha1/shared_types.go +++ b/api/v1alpha1/shared_types.go @@ -262,6 +262,12 @@ type KubernetesServiceSpec struct { // +optional Annotations map[string]string `json:"annotations,omitempty"` + // Labels that should be appended to the service. + // By default, no labels are appended. + // + // +optional + Labels map[string]string `json:"labels,omitempty"` + // Type determines how the Service is exposed. Defaults to LoadBalancer. // Valid options are ClusterIP, LoadBalancer and NodePort. // "LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it). diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ed5df681ad2..a72706c33bb 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -3580,6 +3580,13 @@ func (in *KubernetesServiceSpec) DeepCopyInto(out *KubernetesServiceSpec) { (*out)[key] = val } } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.Type != nil { in, out := &in.Type, &out.Type *out = new(ServiceType) diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index 1b18890cd27..487f436ab81 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -10134,6 +10134,13 @@ spec: - Local - Cluster type: string + labels: + additionalProperties: + type: string + description: |- + Labels that should be appended to the service. + By default, no labels are appended. + type: object loadBalancerClass: description: |- LoadBalancerClass, when specified, allows for choosing the LoadBalancer provider diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider.go b/internal/infrastructure/kubernetes/proxy/resource_provider.go index 768ed7514ba..233afddce73 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_provider.go +++ b/internal/infrastructure/kubernetes/proxy/resource_provider.go @@ -101,10 +101,10 @@ func (r *ResourceRender) Service() (*corev1.Service, error) { } } - // Set the labels based on the owning gatewayclass name. - labels := envoyLabels(r.infra.GetProxyMetadata().Labels) - if OwningGatewayLabelsAbsent(labels) { - return nil, fmt.Errorf("missing owning gateway labels") + // Set the infraLabels based on the owning gatewayclass name. + infraLabels := envoyLabels(r.infra.GetProxyMetadata().Labels) + if OwningGatewayLabelsAbsent(infraLabels) { + return nil, fmt.Errorf("missing owning gateway infraLabels") } // Get annotations @@ -120,10 +120,20 @@ func (r *ResourceRender) Service() (*corev1.Service, error) { annotations = nil } + // Get service-specific labels + svcLabels := map[string]string{} + maps.Copy(svcLabels, infraLabels) + if envoyServiceConfig.Labels != nil { + maps.Copy(svcLabels, envoyServiceConfig.Labels) + } + if len(svcLabels) == 0 { + svcLabels = nil + } + // Set the spec of gateway service serviceSpec := resource.ExpectedServiceSpec(envoyServiceConfig) serviceSpec.Ports = ports - serviceSpec.Selector = resource.GetSelector(labels).MatchLabels + serviceSpec.Selector = resource.GetSelector(infraLabels).MatchLabels if (*envoyServiceConfig.Type) == egv1a1.ServiceTypeClusterIP { if len(r.infra.Addresses) > 0 { @@ -144,7 +154,7 @@ func (r *ResourceRender) Service() (*corev1.Service, error) { }, ObjectMeta: metav1.ObjectMeta{ Namespace: r.Namespace, - Labels: labels, + Labels: svcLabels, Annotations: annotations, }, Spec: serviceSpec, diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider_test.go b/internal/infrastructure/kubernetes/proxy/resource_provider_test.go index c92d94d4b42..8c4138a3825 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_provider_test.go +++ b/internal/infrastructure/kubernetes/proxy/resource_provider_test.go @@ -1051,6 +1051,9 @@ func TestService(t *testing.T) { caseName: "custom", infra: newTestInfra(), service: &egv1a1.KubernetesServiceSpec{ + Labels: map[string]string{ + "key1": "value1", + }, Annotations: map[string]string{ "key1": "value1", }, @@ -1079,6 +1082,31 @@ func TestService(t *testing.T) { }, }, }, + { + caseName: "with-svc-labels", + infra: newTestInfra(), + service: &egv1a1.KubernetesServiceSpec{ + Labels: map[string]string{ + "label1": "value1", + "label2": "value2", + }, + }, + }, + { + caseName: "override-labels", + infra: newTestInfraWithAnnotationsAndLabels(map[string]string{ + "anno1": "value1", + "anno2": "value2", + }, map[string]string{ + "label1": "value1", + "label2": "value2", + }), + service: &egv1a1.KubernetesServiceSpec{ + Labels: map[string]string{ + "label1": "value1-override", + }, + }, + }, { caseName: "clusterIP-custom-addresses", infra: newTestInfraWithAddresses([]string{ diff --git a/internal/infrastructure/kubernetes/proxy/testdata/services/custom.yaml b/internal/infrastructure/kubernetes/proxy/testdata/services/custom.yaml index e898ccb1aff..d087bf24bf6 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/services/custom.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/services/custom.yaml @@ -4,6 +4,7 @@ metadata: annotations: key1: value1 labels: + key1: value1 app.kubernetes.io/name: envoy app.kubernetes.io/component: proxy app.kubernetes.io/managed-by: envoy-gateway diff --git a/internal/infrastructure/kubernetes/proxy/testdata/services/override-labels.yaml b/internal/infrastructure/kubernetes/proxy/testdata/services/override-labels.yaml new file mode 100644 index 00000000000..6f60f58176c --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/services/override-labels.yaml @@ -0,0 +1,37 @@ +apiVersion: v1 +kind: Service +metadata: + annotations: + anno1: value1 + anno2: value2 + labels: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + label1: value1-override + label2: value2 + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + externalTrafficPolicy: Local + ports: + - name: EnvoyHTTPPort + port: 0 + protocol: TCP + targetPort: 8080 + - name: EnvoyHTTPSPort + port: 0 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + label1: value1 + label2: value2 + sessionAffinity: None + type: LoadBalancer diff --git a/internal/infrastructure/kubernetes/proxy/testdata/services/with-svc-labels.yaml b/internal/infrastructure/kubernetes/proxy/testdata/services/with-svc-labels.yaml new file mode 100644 index 00000000000..8ff9e5bb319 --- /dev/null +++ b/internal/infrastructure/kubernetes/proxy/testdata/services/with-svc-labels.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + label1: value1 + label2: value2 + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + name: envoy-default-37a8eec1 + namespace: envoy-gateway-system +spec: + externalTrafficPolicy: Local + ports: + - name: EnvoyHTTPPort + port: 0 + protocol: TCP + targetPort: 8080 + - name: EnvoyHTTPSPort + port: 0 + protocol: TCP + targetPort: 8443 + selector: + app.kubernetes.io/name: envoy + app.kubernetes.io/component: proxy + app.kubernetes.io/managed-by: envoy-gateway + gateway.envoyproxy.io/owning-gateway-name: default + gateway.envoyproxy.io/owning-gateway-namespace: default + sessionAffinity: None + type: LoadBalancer diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index c26a8c713f8..76adfb15735 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -2564,6 +2564,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | | `annotations` | _object (keys:string, values:string)_ | false | Annotations that should be appended to the service.
By default, no annotations are appended. | +| `labels` | _object (keys:string, values:string)_ | false | Labels that should be appended to the service.
By default, no labels are appended. | | `type` | _[ServiceType](#servicetype)_ | false | Type determines how the Service is exposed. Defaults to LoadBalancer.
Valid options are ClusterIP, LoadBalancer and NodePort.
"LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it).
"ClusterIP" means a service will only be accessible inside the cluster, via the cluster IP.
"NodePort" means a service will be exposed on a static Port on all Nodes of the cluster. | | `loadBalancerClass` | _string_ | false | LoadBalancerClass, when specified, allows for choosing the LoadBalancer provider
implementation if more than one are available or is otherwise expected to be specified | | `allocateLoadBalancerNodePorts` | _boolean_ | false | AllocateLoadBalancerNodePorts defines if NodePorts will be automatically allocated for
services with type LoadBalancer. Default is "true". It may be set to "false" if the cluster
load-balancer does not rely on NodePorts. If the caller requests specific NodePorts (by specifying a
value), those requests will be respected, regardless of this field. This field may only be set for
services with type LoadBalancer and will be cleared if the type is changed to any other type. | diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index c26a8c713f8..76adfb15735 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -2564,6 +2564,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | | `annotations` | _object (keys:string, values:string)_ | false | Annotations that should be appended to the service.
By default, no annotations are appended. | +| `labels` | _object (keys:string, values:string)_ | false | Labels that should be appended to the service.
By default, no labels are appended. | | `type` | _[ServiceType](#servicetype)_ | false | Type determines how the Service is exposed. Defaults to LoadBalancer.
Valid options are ClusterIP, LoadBalancer and NodePort.
"LoadBalancer" means a service will be exposed via an external load balancer (if the cloud provider supports it).
"ClusterIP" means a service will only be accessible inside the cluster, via the cluster IP.
"NodePort" means a service will be exposed on a static Port on all Nodes of the cluster. | | `loadBalancerClass` | _string_ | false | LoadBalancerClass, when specified, allows for choosing the LoadBalancer provider
implementation if more than one are available or is otherwise expected to be specified | | `allocateLoadBalancerNodePorts` | _boolean_ | false | AllocateLoadBalancerNodePorts defines if NodePorts will be automatically allocated for
services with type LoadBalancer. Default is "true". It may be set to "false" if the cluster
load-balancer does not rely on NodePorts. If the caller requests specific NodePorts (by specifying a
value), those requests will be respected, regardless of this field. This field may only be set for
services with type LoadBalancer and will be cleared if the type is changed to any other type. | From e0455acdd6dc598a30a35cc90e481f9f81431bc1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 10:42:46 +0800 Subject: [PATCH 09/56] build(deps): bump helm.sh/helm/v3 from 3.16.1 to 3.16.2 (#4436) Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.16.1 to 3.16.2. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.16.1...v3.16.2) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e34c38bed54..956bf2831c6 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( golang.org/x/sys v0.26.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.16.1 + helm.sh/helm/v3 v3.16.2 k8s.io/api v0.31.1 k8s.io/apiextensions-apiserver v0.31.1 k8s.io/apimachinery v0.31.1 diff --git a/go.sum b/go.sum index 01eea5533e5..d26d6608694 100644 --- a/go.sum +++ b/go.sum @@ -1146,8 +1146,8 @@ gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -helm.sh/helm/v3 v3.16.1 h1:cER6tI/8PgUAsaJaQCVBUg3VI9KN4oVaZJgY60RIc0c= -helm.sh/helm/v3 v3.16.1/go.mod h1:r+xBHHP20qJeEqtvBXMf7W35QDJnzY/eiEBzt+TfHps= +helm.sh/helm/v3 v3.16.2 h1:Y9v7ry+ubQmi+cb5zw1Llx8OKHU9Hk9NQ/+P+LGBe2o= +helm.sh/helm/v3 v3.16.2/go.mod h1:SyTXgKBjNqi2NPsHCW5dDAsHqvGIu0kdNYNH9gQaw70= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 045a438c10ae3b3a60185065ab381ed178c0eed2 Mon Sep 17 00:00:00 2001 From: Puerco Date: Mon, 14 Oct 2024 22:30:11 -0600 Subject: [PATCH 10/56] docs: Fix capital D in ratelimit example (#4428) --- site/content/en/docs/tasks/traffic/global-rate-limit.md | 2 +- site/content/en/latest/tasks/traffic/global-rate-limit.md | 2 +- site/content/en/v1.1/tasks/traffic/global-rate-limit.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/site/content/en/docs/tasks/traffic/global-rate-limit.md b/site/content/en/docs/tasks/traffic/global-rate-limit.md index bb87c47de49..da00334b296 100644 --- a/site/content/en/docs/tasks/traffic/global-rate-limit.md +++ b/site/content/en/docs/tasks/traffic/global-rate-limit.md @@ -871,7 +871,7 @@ spec: - clientSelectors: - sourceCIDR: value: 0.0.0.0/0 - type: distinct + type: Distinct limit: requests: 3 unit: Hour diff --git a/site/content/en/latest/tasks/traffic/global-rate-limit.md b/site/content/en/latest/tasks/traffic/global-rate-limit.md index f105de880cd..6c96b12efe7 100644 --- a/site/content/en/latest/tasks/traffic/global-rate-limit.md +++ b/site/content/en/latest/tasks/traffic/global-rate-limit.md @@ -870,7 +870,7 @@ spec: - clientSelectors: - sourceCIDR: value: 0.0.0.0/0 - type: distinct + type: Distinct limit: requests: 3 unit: Hour diff --git a/site/content/en/v1.1/tasks/traffic/global-rate-limit.md b/site/content/en/v1.1/tasks/traffic/global-rate-limit.md index bb87c47de49..da00334b296 100644 --- a/site/content/en/v1.1/tasks/traffic/global-rate-limit.md +++ b/site/content/en/v1.1/tasks/traffic/global-rate-limit.md @@ -871,7 +871,7 @@ spec: - clientSelectors: - sourceCIDR: value: 0.0.0.0/0 - type: distinct + type: Distinct limit: requests: 3 unit: Hour From a6beeed0552ba7a5a4dcae29854bc20ced759433 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:37:57 +0800 Subject: [PATCH 11/56] build(deps): bump google.golang.org/protobuf from 1.34.2 to 1.35.1 in /examples/extension-server (#4439) build(deps): bump google.golang.org/protobuf Bumps google.golang.org/protobuf from 1.34.2 to 1.35.1. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Huabing Zhao --- examples/extension-server/go.mod | 2 +- examples/extension-server/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/extension-server/go.mod b/examples/extension-server/go.mod index d6f03677ed6..75d36f57857 100644 --- a/examples/extension-server/go.mod +++ b/examples/extension-server/go.mod @@ -7,7 +7,7 @@ require ( github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568 github.com/urfave/cli/v2 v2.27.4 google.golang.org/grpc v1.67.1 - google.golang.org/protobuf v1.34.2 + google.golang.org/protobuf v1.35.1 k8s.io/apimachinery v0.31.1 sigs.k8s.io/controller-runtime v0.19.0 sigs.k8s.io/gateway-api v1.2.0 diff --git a/examples/extension-server/go.sum b/examples/extension-server/go.sum index fd1ecc8f322..e987ca82ce8 100644 --- a/examples/extension-server/go.sum +++ b/examples/extension-server/go.sum @@ -111,8 +111,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From 14aadc7d4a23c7f0ed66d5f011ef1486b6a27b88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:04:52 +0800 Subject: [PATCH 12/56] build(deps): bump github.com/prometheus/common from 0.59.1 to 0.60.0 (#4438) Bumps [github.com/prometheus/common](https://github.com/prometheus/common) from 0.59.1 to 0.60.0. - [Release notes](https://github.com/prometheus/common/releases) - [Changelog](https://github.com/prometheus/common/blob/main/RELEASE.md) - [Commits](https://github.com/prometheus/common/compare/v0.59.1...v0.60.0) --- updated-dependencies: - dependency-name: github.com/prometheus/common dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Huabing Zhao --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 956bf2831c6..416c6bc01b5 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/miekg/dns v1.1.62 github.com/ohler55/ojg v1.24.1 github.com/prometheus/client_golang v1.20.4 - github.com/prometheus/common v0.59.1 + github.com/prometheus/common v0.60.0 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 @@ -274,7 +274,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.30.0 - golang.org/x/oauth2 v0.22.0 // indirect + golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/term v0.25.0 // indirect golang.org/x/text v0.19.0 // indirect diff --git a/go.sum b/go.sum index d26d6608694..7f32941ab83 100644 --- a/go.sum +++ b/go.sum @@ -701,8 +701,8 @@ github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7q github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0= -github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0= +github.com/prometheus/common v0.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= +github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -981,8 +981,8 @@ golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From 8f0131c50dcaaafae0c30a1f98eeb6a1ed7aee8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:49:50 +0800 Subject: [PATCH 13/56] build(deps): bump github.com/replicatedhq/troubleshoot from 0.105.2 to 0.107.0 (#4437) build(deps): bump github.com/replicatedhq/troubleshoot Bumps [github.com/replicatedhq/troubleshoot](https://github.com/replicatedhq/troubleshoot) from 0.105.2 to 0.107.0. - [Release notes](https://github.com/replicatedhq/troubleshoot/releases) - [Commits](https://github.com/replicatedhq/troubleshoot/compare/v0.105.2...v0.107.0) --- updated-dependencies: - dependency-name: github.com/replicatedhq/troubleshoot dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Huabing Zhao --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 416c6bc01b5..b9599f0714e 100644 --- a/go.mod +++ b/go.mod @@ -63,7 +63,7 @@ require ( require ( github.com/docker/docker v27.3.1+incompatible - github.com/replicatedhq/troubleshoot v0.105.2 + github.com/replicatedhq/troubleshoot v0.107.0 google.golang.org/grpc v1.67.1 sigs.k8s.io/kubectl-validate v0.0.5-0.20240827210056-ce13d95db263 ) diff --git a/go.sum b/go.sum index 7f32941ab83..8008b00e8e6 100644 --- a/go.sum +++ b/go.sum @@ -717,8 +717,8 @@ github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= -github.com/replicatedhq/troubleshoot v0.105.2 h1:Fm1kKdzhPw+J7UnpLByxYJ3XAVV4IkylUIDt0eaUTFE= -github.com/replicatedhq/troubleshoot v0.105.2/go.mod h1:WqquTbNHLnZiSWsu6Mzo3rwez5kZ/A+1Hq4K/yq0HBo= +github.com/replicatedhq/troubleshoot v0.107.0 h1:AzaKBxNKuIQpERyitjJ4EINZTz4vlUacW5QguZZkTww= +github.com/replicatedhq/troubleshoot v0.107.0/go.mod h1:AjaLpi9kidsFAuyAvBrvcG04w49WIDm7Iy6XyMVMv6U= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= From 838cd27771b1a0ba29e0a584ba9e68884109337b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:10:11 +0800 Subject: [PATCH 14/56] build(deps): bump github.com/bufbuild/buf from 1.44.0 to 1.45.0 in /tools/src/buf (#4440) build(deps): bump github.com/bufbuild/buf in /tools/src/buf Bumps [github.com/bufbuild/buf](https://github.com/bufbuild/buf) from 1.44.0 to 1.45.0. - [Release notes](https://github.com/bufbuild/buf/releases) - [Changelog](https://github.com/bufbuild/buf/blob/main/CHANGELOG.md) - [Commits](https://github.com/bufbuild/buf/compare/v1.44.0...v1.45.0) --- updated-dependencies: - dependency-name: github.com/bufbuild/buf dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Huabing Zhao --- tools/src/buf/go.mod | 29 +++++++++++----------- tools/src/buf/go.sum | 58 +++++++++++++++++++++++--------------------- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/tools/src/buf/go.mod b/tools/src/buf/go.mod index 9154ce02a93..b2022d8afde 100644 --- a/tools/src/buf/go.mod +++ b/tools/src/buf/go.mod @@ -2,7 +2,7 @@ module local go 1.23.1 -require github.com/bufbuild/buf v1.44.0 +require github.com/bufbuild/buf v1.45.0 require ( buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.34.2-20240928190436-5e8abcfd7a7e.2 // indirect @@ -21,7 +21,7 @@ require ( github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/bufbuild/protocompile v0.14.1 // indirect github.com/bufbuild/protoplugin v0.0.0-20240911180120-7bb73e41a54a // indirect - github.com/bufbuild/protovalidate-go v0.7.1 // indirect + github.com/bufbuild/protovalidate-go v0.7.2 // indirect github.com/containerd/cgroups/v3 v3.0.3 // indirect github.com/containerd/containerd v1.7.22 // indirect github.com/containerd/continuity v0.4.3 // indirect @@ -50,7 +50,7 @@ require ( github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/cel-go v0.21.0 // indirect github.com/google/go-containerregistry v0.20.2 // indirect - github.com/google/pprof v0.0.0-20240929191954-255acd752d31 // indirect + github.com/google/pprof v0.0.0-20241001023024-f4c0cfd0cf1d // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -87,7 +87,7 @@ require ( github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect - github.com/tetratelabs/wazero v1.8.0 // indirect + github.com/tetratelabs/wazero v1.8.1 // indirect github.com/vbatts/tar-split v0.11.6 // indirect go.lsp.dev/jsonrpc2 v0.10.0 // indirect go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 // indirect @@ -104,18 +104,19 @@ require ( go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.27.0 // indirect - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect + go.uber.org/zap/exp v0.1.1-0.20240913022758-ede8e1888f83 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 // indirect golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.29.0 // indirect + golang.org/x/net v0.30.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/term v0.24.0 // indirect - golang.org/x/text v0.18.0 // indirect - golang.org/x/tools v0.25.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240924160255-9d4c2d233b61 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 // indirect - google.golang.org/grpc v1.67.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/term v0.25.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.26.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect + google.golang.org/grpc v1.67.1 // indirect google.golang.org/protobuf v1.34.3-0.20240906163944-03df6c145d96 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect pluginrpc.com/pluginrpc v0.5.0 // indirect diff --git a/tools/src/buf/go.sum b/tools/src/buf/go.sum index 1c3d37804b5..6fb21576d0e 100644 --- a/tools/src/buf/go.sum +++ b/tools/src/buf/go.sum @@ -30,14 +30,14 @@ github.com/Microsoft/hcsshim v0.12.7 h1:MP6R1spmjxTE4EU4J3YsrTxn8CjvN9qwjTKJXldF github.com/Microsoft/hcsshim v0.12.7/go.mod h1:HPbAuJ9BvQYYZbB4yEQcyGIsTP5L4yHKeO9XO149AEM= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= -github.com/bufbuild/buf v1.44.0 h1:95vd2ZbgaDkKIJ557eZrx2z6jdMULEpVbNpLlw/9Y5w= -github.com/bufbuild/buf v1.44.0/go.mod h1:Ksh+C0pR3t1or0BGyzq7krkam3zhBv21QVs1zsqdOik= +github.com/bufbuild/buf v1.45.0 h1:WdaM5OCjqEURmzOiz3h9gVilFXqWpt6X+zbOVqKti1A= +github.com/bufbuild/buf v1.45.0/go.mod h1:j+GjGIKS+CvubKtPiC0KpEiHAd3wS9/5sn2/U5WlA20= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= github.com/bufbuild/protoplugin v0.0.0-20240911180120-7bb73e41a54a h1:l3RhVoG0RtC61h6TVWnkniGj4TgBebuyPQRdleFAmTg= github.com/bufbuild/protoplugin v0.0.0-20240911180120-7bb73e41a54a/go.mod h1:c5D8gWRIZ2HLWO3gXYTtUfw/hbJyD8xikv2ooPxnklQ= -github.com/bufbuild/protovalidate-go v0.7.1 h1:ac50NTO6+1+mKg5sP/GBPLlMkQFeI+OeaYGFdS1vu98= -github.com/bufbuild/protovalidate-go v0.7.1/go.mod h1:PHV5pFuWlRzdDW02/cmVyNzdiQ+RNNwo7idGxdzS7o4= +github.com/bufbuild/protovalidate-go v0.7.2 h1:UuvKyZHl5p7u3ztEjtRtqtDxOjRKX5VUOgKFq6p6ETk= +github.com/bufbuild/protovalidate-go v0.7.2/go.mod h1:PHV5pFuWlRzdDW02/cmVyNzdiQ+RNNwo7idGxdzS7o4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -147,8 +147,8 @@ github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/pprof v0.0.0-20240929191954-255acd752d31 h1:LcRdQWywSgfi5jPsYZ1r2avbbs5IQ5wtyhMBCcokyo4= -github.com/google/pprof v0.0.0-20240929191954-255acd752d31/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241001023024-f4c0cfd0cf1d h1:Jaz2JzpQaQXyET0AjLBXShrthbpqMkhGiEfkcQAiAUs= +github.com/google/pprof v0.0.0-20241001023024-f4c0cfd0cf1d/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -254,8 +254,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tetratelabs/wazero v1.8.0 h1:iEKu0d4c2Pd+QSRieYbnQC9yiFlMS9D+Jr0LsRmcF4g= -github.com/tetratelabs/wazero v1.8.0/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= +github.com/tetratelabs/wazero v1.8.1 h1:NrcgVbWfkWvVc4UtT4LRLDf91PsOzDzefMdwhLfA550= +github.com/tetratelabs/wazero v1.8.1/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -298,14 +298,16 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap/exp v0.1.1-0.20240913022758-ede8e1888f83 h1:wpjRiPjppWaUIH+GC0bRvsdaH2K4Dw49dEJa7MX01Mk= +go.uber.org/zap/exp v0.1.1-0.20240913022758-ede8e1888f83/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6 h1:1wqE9dj9NpSm04INVsJhhEUzhuDVjbcyKH91sVyPATw= +golang.org/x/exp v0.0.0-20241004190924-225e2abe05e6/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -322,8 +324,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -342,14 +344,14 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -360,8 +362,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= -golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -371,17 +373,17 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/api v0.0.0-20240924160255-9d4c2d233b61 h1:pAjq8XSSzXoP9ya73v/w+9QEAAJNluLrpmMq5qFJQNY= -google.golang.org/genproto/googleapis/api v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:O6rP0uBq4k0mdi/b4ZEMAZjkhYWhS815kCvaMha4VN8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 h1:N9BgCIAUvn/M+p4NJccWPWb3BWh88+zyL0ll9HgbEeM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f h1:jTm13A2itBi3La6yTGqn8bVSrc3ZZ1r8ENHlIXBfnRA= +google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f/go.mod h1:CLGoBuH1VHxAUXVPP8FfPwPEVJB6lz3URE5mY2SuayE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f h1:cUMEy+8oS78BWIH9OWazBkzbr090Od9tWBNtZHkOhf0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= -google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= From cec67a86ac7e690b853eaf479a9a1127850c0b3b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:10:33 +0800 Subject: [PATCH 15/56] build(deps): bump the go-opentelemetry-io group across 1 directory with 8 updates (#4434) * build(deps): bump the go-opentelemetry-io group across 1 directory with 8 updates Bumps the go-opentelemetry-io group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.30.0` | `1.31.0` | | [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc](https://github.com/open-telemetry/opentelemetry-go) | `1.30.0` | `1.31.0` | | [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp](https://github.com/open-telemetry/opentelemetry-go) | `1.30.0` | `1.31.0` | | [go.opentelemetry.io/otel/exporters/prometheus](https://github.com/open-telemetry/opentelemetry-go) | `0.52.0` | `0.53.0` | | [go.opentelemetry.io/otel/exporters/stdout/stdoutmetric](https://github.com/open-telemetry/opentelemetry-go) | `1.30.0` | `1.31.0` | Updates `go.opentelemetry.io/otel` from 1.30.0 to 1.31.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.30.0...v1.31.0) Updates `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` from 1.30.0 to 1.31.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.30.0...v1.31.0) Updates `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` from 1.30.0 to 1.31.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.30.0...v1.31.0) Updates `go.opentelemetry.io/otel/exporters/prometheus` from 0.52.0 to 0.53.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/example/prometheus/v0.52.0...example/prometheus/v0.53.0) Updates `go.opentelemetry.io/otel/exporters/stdout/stdoutmetric` from 1.30.0 to 1.31.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.30.0...v1.31.0) Updates `go.opentelemetry.io/otel/metric` from 1.30.0 to 1.31.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.30.0...v1.31.0) Updates `go.opentelemetry.io/otel/sdk/metric` from 1.30.0 to 1.31.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.30.0...v1.31.0) Updates `go.opentelemetry.io/otel/sdk` from 1.30.0 to 1.31.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.30.0...v1.31.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel/exporters/prometheus dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel/exporters/stdout/stdoutmetric dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel/metric dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel/sdk/metric dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io - dependency-name: go.opentelemetry.io/otel/sdk dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-opentelemetry-io ... Signed-off-by: dependabot[bot] * update Signed-off-by: zirain --------- Signed-off-by: dependabot[bot] Signed-off-by: zirain Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: zirain Co-authored-by: Huabing Zhao --- examples/extension-server/go.mod | 4 +-- examples/extension-server/go.sum | 8 ++--- go.mod | 24 +++++++-------- go.sum | 52 ++++++++++++++++---------------- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/examples/extension-server/go.mod b/examples/extension-server/go.mod index 75d36f57857..c4a08fecafc 100644 --- a/examples/extension-server/go.mod +++ b/examples/extension-server/go.mod @@ -34,8 +34,8 @@ require ( golang.org/x/net v0.30.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240924160255-9d4c2d233b61 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect diff --git a/examples/extension-server/go.sum b/examples/extension-server/go.sum index e987ca82ce8..7d995a60424 100644 --- a/examples/extension-server/go.sum +++ b/examples/extension-server/go.sum @@ -105,10 +105,10 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20240924160255-9d4c2d233b61 h1:pAjq8XSSzXoP9ya73v/w+9QEAAJNluLrpmMq5qFJQNY= -google.golang.org/genproto/googleapis/api v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:O6rP0uBq4k0mdi/b4ZEMAZjkhYWhS815kCvaMha4VN8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 h1:N9BgCIAUvn/M+p4NJccWPWb3BWh88+zyL0ll9HgbEeM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= diff --git a/go.mod b/go.mod index b9599f0714e..1a235360cd7 100644 --- a/go.mod +++ b/go.mod @@ -34,18 +34,18 @@ require ( github.com/stretchr/testify v1.9.0 github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7 github.com/tsaarni/certyaml v0.9.3 - go.opentelemetry.io/otel v1.30.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.30.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.30.0 - go.opentelemetry.io/otel/exporters/prometheus v0.52.0 - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.30.0 - go.opentelemetry.io/otel/metric v1.30.0 - go.opentelemetry.io/otel/sdk/metric v1.30.0 + go.opentelemetry.io/otel v1.31.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 + go.opentelemetry.io/otel/exporters/prometheus v0.53.0 + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.31.0 + go.opentelemetry.io/otel/metric v1.31.0 + go.opentelemetry.io/otel/sdk/metric v1.31.0 go.opentelemetry.io/proto/otlp v1.3.1 go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e golang.org/x/sys v0.26.0 - google.golang.org/protobuf v1.34.2 + google.golang.org/protobuf v1.35.1 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.16.2 k8s.io/api v0.31.1 @@ -268,8 +268,8 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/tsaarni/x500dn v1.0.0 // indirect github.com/xlab/treeprint v1.2.0 // indirect - go.opentelemetry.io/otel/sdk v1.30.0 - go.opentelemetry.io/otel/trace v1.30.0 // indirect + go.opentelemetry.io/otel/sdk v1.31.0 + go.opentelemetry.io/otel/trace v1.31.0 // indirect go.starlark.net v0.0.0-20240520160348-046347dcd104 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.21.0 // indirect @@ -281,8 +281,8 @@ require ( golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/component-base v0.31.1 // indirect diff --git a/go.sum b/go.sum index 8008b00e8e6..c5d453f9e0d 100644 --- a/go.sum +++ b/go.sum @@ -723,8 +723,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rubenv/sql-migrate v1.7.0 h1:HtQq1xyTN2ISmQDggnh0c9U3JlP8apWh8YO2jzlXpTI= github.com/rubenv/sql-migrate v1.7.0/go.mod h1:S4wtDEG1CKn+0ShpTtzWhFpHHI5PvCUtiGI+C+Z2THE= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= @@ -888,32 +888,32 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.5 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= -go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.30.0 h1:WypxHH02KX2poqqbaadmkMYalGyy/vil4HE4PM4nRJc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.30.0/go.mod h1:U79SV99vtvGSEBeeHnpgGJfTsnsdkWLpPN/CcHAzBSI= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.30.0 h1:VrMAbeJz4gnVDg2zEzjHG4dEH86j4jO6VYB+NgtGD8s= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.30.0/go.mod h1:qqN/uFdpeitTvm+JDqqnjm517pmQRYxTORbETHq5tOc= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0 h1:FZ6ei8GFW7kyPYdxJaV2rgI6M+4tvZzhYsQ2wgyVC08= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.31.0/go.mod h1:MdEu/mC6j3D+tTEfvI15b5Ci2Fn7NneJ71YMoiS3tpI= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0 h1:ZsXq73BERAiNuuFXYqP4MR5hBrjXfMGSO+Cx7qoOZiM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.31.0/go.mod h1:hg1zaDMpyZJuUzjFxFsRYBoccE86tM9Uf4IqNMUxvrY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0/go.mod h1:/OpE/y70qVkndM0TrxT4KBoN3RsFZP0QaofcfYrj76I= -go.opentelemetry.io/otel/exporters/prometheus v0.52.0 h1:kmU3H0b9ufFSi8IQCcxack+sWUblKkFbqWYs6YiACGQ= -go.opentelemetry.io/otel/exporters/prometheus v0.52.0/go.mod h1:+wsAp2+JhuGXX7YRkjlkx6hyWY3ogFPfNA4x3nyiAh0= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.30.0 h1:IyFlqNsi8VT/nwYlLJfdM0y1gavxGpEvnf6FtVfZ6X4= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.30.0/go.mod h1:bxiX8eUeKoAEQmbq/ecUT8UqZwCjZW52yJrXJUSozsk= +go.opentelemetry.io/otel/exporters/prometheus v0.53.0 h1:QXobPHrwiGLM4ufrY3EOmDPJpo2P90UuFau4CDPJA/I= +go.opentelemetry.io/otel/exporters/prometheus v0.53.0/go.mod h1:WOAXGr3D00CfzmFxtTV1eR0GpoHuPEu+HJT8UWW2SIU= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.31.0 h1:HZgBIps9wH0RDrwjrmNa3DVbNRW60HEhdzqZFyAp3fI= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.31.0/go.mod h1:RDRhvt6TDG0eIXmonAx5bd9IcwpqCkziwkOClzWKwAQ= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 h1:VhlEQAPp9R1ktYfrPk5SOryw1e9LDDTZCbIPFrho0ec= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0/go.mod h1:kB3ufRbfU+CQ4MlUcqtW8Z7YEOBeK2DJ6CmR5rYYF3E= -go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= -go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= -go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE= -go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg= -go.opentelemetry.io/otel/sdk/metric v1.30.0 h1:QJLT8Pe11jyHBHfSAgYH7kEmT24eX792jZO1bo4BXkM= -go.opentelemetry.io/otel/sdk/metric v1.30.0/go.mod h1:waS6P3YqFNzeP01kuo/MBBYqaoBJl7efRQHOaydhy1Y= -go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= -go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.starlark.net v0.0.0-20240520160348-046347dcd104 h1:3qhteRISupnJvaWshOmeqEUs2y9oc/+/ePPvDh3Eygg= @@ -1082,10 +1082,10 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= -google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= -google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 h1:N9BgCIAUvn/M+p4NJccWPWb3BWh88+zyL0ll9HgbEeM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -1105,8 +1105,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 1f29518dc2472d3f904d07eb93778675a4bb2ea3 Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Tue, 15 Oct 2024 08:42:49 -0500 Subject: [PATCH 16/56] feat(translator): client tls session resumption (#4293) * api: client tls session resumption Signed-off-by: Guy Daich * change api Signed-off-by: Guy Daich * add cel Signed-off-by: Guy Daich * implement tls session Signed-off-by: Guy Daich * fix gen Signed-off-by: Guy Daich * fix e2e Signed-off-by: Guy Daich * review fixes Signed-off-by: Guy Daich * rm sessionTimeout, use session container and shorter names Signed-off-by: Guy Daich * fix review comments Signed-off-by: Guy Daich * fix comment Signed-off-by: Guy Daich * fix gen Signed-off-by: Guy Daich --------- Signed-off-by: Guy Daich Co-authored-by: zirain --- api/v1alpha1/tls_types.go | 41 ++ api/v1alpha1/zz_generated.deepcopy.go | 80 +++ ...y.envoyproxy.io_clienttrafficpolicies.yaml | 21 + internal/gatewayapi/clienttrafficpolicy.go | 9 + .../clienttrafficpolicy-tls-settings.in.yaml | 4 + .../clienttrafficpolicy-tls-settings.out.yaml | 6 + internal/ir/xds.go | 4 + internal/xds/translator/listener.go | 16 + .../tls-with-ciphers-versions-alpn.yaml | 2 + .../testdata/out/xds-ir/http3.listeners.yaml | 4 + .../jsonpatch-with-jsonpath.listeners.yaml | 2 + .../out/xds-ir/jsonpatch.listeners.yaml | 2 + .../listener-proxy-protocol.listeners.yaml | 2 + .../xds-ir/mixed-tls-jwt-authn.listeners.yaml | 2 + ...ultiple-listeners-same-port.listeners.yaml | 4 + ...ertificate-with-custom-data.listeners.yaml | 10 + ...-forward-client-certificate.listeners.yaml | 10 + ...client-certificate-disabled.listeners.yaml | 4 + .../out/xds-ir/mutual-tls.listeners.yaml | 4 + .../out/xds-ir/simple-tls.listeners.yaml | 2 + .../suppress-envoy-headers.listeners.yaml | 2 + .../tcp-route-tls-terminate.listeners.yaml | 4 + ...-with-ciphers-versions-alpn.listeners.yaml | 2 + site/content/en/latest/api/extension_types.md | 61 ++ .../latest/tasks/security/secure-gateways.md | 80 +++ site/content/zh/latest/api/extension_types.md | 61 ++ test/e2e/base/manifests.yaml | 546 +++++++++--------- test/e2e/testdata/client-mtls.yaml | 78 +++ test/e2e/tests/client_mtls.go | 170 ++++++ 29 files changed, 960 insertions(+), 273 deletions(-) diff --git a/api/v1alpha1/tls_types.go b/api/v1alpha1/tls_types.go index 38c52761125..b926558c525 100644 --- a/api/v1alpha1/tls_types.go +++ b/api/v1alpha1/tls_types.go @@ -15,6 +15,10 @@ type ClientTLSSettings struct { // +optional ClientValidation *ClientValidationContext `json:"clientValidation,omitempty"` TLSSettings `json:",inline"` + + // Session defines settings related to TLS session management. + // +optional + Session *Session `json:"session,omitempty"` } // +kubebuilder:validation:XValidation:rule="has(self.minVersion) && self.minVersion == '1.3' ? !has(self.ciphers) : true", message="setting ciphers has no effect if the minimum possible TLS version is 1.3" @@ -133,3 +137,40 @@ type ClientValidationContext struct { // +optional CACertificateRefs []gwapiv1.SecretObjectReference `json:"caCertificateRefs,omitempty"` } + +// Session defines settings related to TLS session management. +type Session struct { + // Resumption determines the proxy's supported TLS session resumption option. + // By default, Envoy Gateway does not enable session resumption. Use sessionResumption to + // enable stateful and stateless session resumption. Users should consider security impacts + // of different resumption methods. Performance gains from resumption are diminished when + // Envoy proxy is deployed with more than one replica. + // +optional + Resumption *SessionResumption `json:"resumption,omitempty"` +} + +// SessionResumption defines supported tls session resumption methods and their associated configuration. +type SessionResumption struct { + // Stateless defines setting for stateless (session-ticket based) session resumption + // +optional + Stateless *StatelessTLSSessionResumption `json:"stateless,omitempty"` + + // Stateful defines setting for stateful (session-id based) session resumption + // +optional + Stateful *StatefulTLSSessionResumption `json:"stateful,omitempty"` +} + +// StatefulTLSSessionResumption defines the stateful (session-id based) type of TLS session resumption. +// Note: When Envoy Proxy is deployed with more than one replica, session caches are not synchronized +// between instances, possibly leading to resumption failures. +// Envoy does not re-validate client certificates upon session resumption. +// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#config-route-v3-routematch-tlscontextmatchoptions +type StatefulTLSSessionResumption struct{} + +// StatelessTLSSessionResumption defines the stateless (session-ticket based) type of TLS session resumption. +// Note: When Envoy Proxy is deployed with more than one replica, session ticket encryption keys are not +// synchronized between instances, possibly leading to resumption failures. +// In-memory session ticket encryption keys are rotated every 48 hours. +// https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/transport_sockets/tls/v3/common.proto#extensions-transport-sockets-tls-v3-tlssessionticketkeys +// https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#Session-tickets +type StatelessTLSSessionResumption struct{} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index a72706c33bb..2ce5a5762ee 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -759,6 +759,11 @@ func (in *ClientTLSSettings) DeepCopyInto(out *ClientTLSSettings) { (*in).DeepCopyInto(*out) } in.TLSSettings.DeepCopyInto(&out.TLSSettings) + if in.Session != nil { + in, out := &in.Session, &out.Session + *out = new(Session) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTLSSettings. @@ -4947,6 +4952,51 @@ func (in *SecurityPolicySpec) DeepCopy() *SecurityPolicySpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Session) DeepCopyInto(out *Session) { + *out = *in + if in.Resumption != nil { + in, out := &in.Resumption, &out.Resumption + *out = new(SessionResumption) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Session. +func (in *Session) DeepCopy() *Session { + if in == nil { + return nil + } + out := new(Session) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SessionResumption) DeepCopyInto(out *SessionResumption) { + *out = *in + if in.Stateless != nil { + in, out := &in.Stateless, &out.Stateless + *out = new(StatelessTLSSessionResumption) + **out = **in + } + if in.Stateful != nil { + in, out := &in.Stateful, &out.Stateful + *out = new(StatefulTLSSessionResumption) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SessionResumption. +func (in *SessionResumption) DeepCopy() *SessionResumption { + if in == nil { + return nil + } + out := new(SessionResumption) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ShutdownConfig) DeepCopyInto(out *ShutdownConfig) { *out = *in @@ -5037,6 +5087,36 @@ func (in *SourceMatch) DeepCopy() *SourceMatch { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StatefulTLSSessionResumption) DeepCopyInto(out *StatefulTLSSessionResumption) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StatefulTLSSessionResumption. +func (in *StatefulTLSSessionResumption) DeepCopy() *StatefulTLSSessionResumption { + if in == nil { + return nil + } + out := new(StatefulTLSSessionResumption) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StatelessTLSSessionResumption) DeepCopyInto(out *StatelessTLSSessionResumption) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StatelessTLSSessionResumption. +func (in *StatelessTLSSessionResumption) DeepCopy() *StatelessTLSSessionResumption { + if in == nil { + return nil + } + out := new(StatelessTLSSessionResumption) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StatusCodeMatch) DeepCopyInto(out *StatusCodeMatch) { *out = *in diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index 81f449b8860..582486e706f 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -808,6 +808,27 @@ spec: - "1.2" - "1.3" type: string + session: + description: Session defines settings related to TLS session management. + properties: + resumption: + description: |- + Resumption determines the proxy's supported TLS session resumption option. + By default, Envoy Gateway does not enable session resumption. Use sessionResumption to + enable stateful and stateless session resumption. Users should consider security impacts + of different resumption methods. Performance gains from resumption are diminished when + Envoy proxy is deployed with more than one replica. + properties: + stateful: + description: Stateful defines setting for stateful (session-id + based) session resumption + type: object + stateless: + description: Stateless defines setting for stateless (session-ticket + based) session resumption + type: object + type: object + type: object signatureAlgorithms: description: |- SignatureAlgorithms specifies which signature algorithms the listener should diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go index 67cd60ad679..bded79d4cf9 100644 --- a/internal/gatewayapi/clienttrafficpolicy.go +++ b/internal/gatewayapi/clienttrafficpolicy.go @@ -864,6 +864,15 @@ func (t *Translator) buildListenerTLSParameters(policy *egv1a1.ClientTrafficPoli } } + if tlsParams.Session != nil && tlsParams.Session.Resumption != nil { + if tlsParams.Session.Resumption.Stateless != nil { + irTLSConfig.StatelessSessionResumption = true + } + if tlsParams.Session.Resumption.Stateful != nil { + irTLSConfig.StatefulSessionResumption = true + } + } + return irTLSConfig, nil } diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-tls-settings.in.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-tls-settings.in.yaml index 59dc2819c7c..bc5878a52f0 100644 --- a/internal/gatewayapi/testdata/clienttrafficpolicy-tls-settings.in.yaml +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-tls-settings.in.yaml @@ -22,6 +22,10 @@ clientTrafficPolicies: signatureAlgorithms: - sig1 - sig2 + session: + resumption: + stateless: {} + stateful: {} gateways: - apiVersion: gateway.networking.k8s.io/v1 kind: Gateway diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-tls-settings.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-tls-settings.out.yaml index f66ef90810d..ed684f328b4 100644 --- a/internal/gatewayapi/testdata/clienttrafficpolicy-tls-settings.out.yaml +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-tls-settings.out.yaml @@ -20,6 +20,10 @@ clientTrafficPolicies: - curve1 maxVersion: "1.3" minVersion: "1.0" + session: + resumption: + stateful: {} + stateless: {} signatureAlgorithms: - sig1 - sig2 @@ -174,6 +178,8 @@ xdsIR: signatureAlgorithms: - sig1 - sig2 + statefulSessionResumption: true + statelessSessionResumption: true - address: 0.0.0.0 hostnames: - '*' diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 53eb34fa2a6..49d6fdbf064 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -351,6 +351,10 @@ type TLSConfig struct { SignatureAlgorithms []string `json:"signatureAlgorithms,omitempty" yaml:"signatureAlgorithms,omitempty"` // ALPNProtocols exposed by this listener ALPNProtocols []string `json:"alpnProtocols,omitempty" yaml:"alpnProtocols,omitempty"` + // StatelessSessionResumption determines if stateless (session-ticket based) session resumption is enabled + StatelessSessionResumption bool `json:"statelessSessionResumption,omitempty" yaml:"statelessSessionResumption,omitempty"` + // StatefulSessionResumption determines if stateful (session-id based) session resumption is enabled + StatefulSessionResumption bool `json:"statefulSessionResumption,omitempty" yaml:"statefulSessionResumption,omitempty"` } // TLSCertificate holds a single certificate's details diff --git a/internal/xds/translator/listener.go b/internal/xds/translator/listener.go index 9cc8e61f6ed..dda639f9f2f 100644 --- a/internal/xds/translator/listener.go +++ b/internal/xds/translator/listener.go @@ -624,6 +624,8 @@ func buildDownstreamQUICTransportSocket(tlsConfig *ir.TLSConfig) (*corev3.Transp } } + setDownstreamTLSSessionSettings(tlsConfig, tlsCtx.DownstreamTlsContext) + tlsCtxAny, err := anypb.New(tlsCtx) if err != nil { return nil, err @@ -664,6 +666,8 @@ func buildXdsDownstreamTLSSocket(tlsConfig *ir.TLSConfig) (*corev3.TransportSock } } + setDownstreamTLSSessionSettings(tlsConfig, tlsCtx) + tlsCtxAny, err := anypb.New(tlsCtx) if err != nil { return nil, err @@ -677,6 +681,18 @@ func buildXdsDownstreamTLSSocket(tlsConfig *ir.TLSConfig) (*corev3.TransportSock }, nil } +func setDownstreamTLSSessionSettings(tlsConfig *ir.TLSConfig, tlsCtx *tlsv3.DownstreamTlsContext) { + if !tlsConfig.StatefulSessionResumption { + tlsCtx.DisableStatefulSessionResumption = true + } + + if !tlsConfig.StatelessSessionResumption { + tlsCtx.SessionTicketKeysType = &tlsv3.DownstreamTlsContext_DisableStatelessSessionResumption{ + DisableStatelessSessionResumption: true, + } + } +} + func buildTLSParams(tlsConfig *ir.TLSConfig) *tlsv3.TlsParameters { p := &tlsv3.TlsParameters{} isEmpty := true diff --git a/internal/xds/translator/testdata/in/xds-ir/tls-with-ciphers-versions-alpn.yaml b/internal/xds/translator/testdata/in/xds-ir/tls-with-ciphers-versions-alpn.yaml index 03e161599b7..afcf3322715 100644 --- a/internal/xds/translator/testdata/in/xds-ir/tls-with-ciphers-versions-alpn.yaml +++ b/internal/xds/translator/testdata/in/xds-ir/tls-with-ciphers-versions-alpn.yaml @@ -30,6 +30,7 @@ http: minVersion: "1.0" alpnProtocols: - some-other-protocol + statefulSessionResumption: true certificates: - name: secret-1 # byte slice representation of "key-data" @@ -107,6 +108,7 @@ tcp: minVersion: "1.0" alpnProtocols: - some-other-protocol + statelessSessionResumption: true certificates: - name: secret-3 # byte slice representation of "key-data" diff --git a/internal/xds/translator/testdata/out/xds-ir/http3.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/http3.listeners.yaml index 56a11c58a99..49a651da85e 100644 --- a/internal/xds/translator/testdata/out/xds-ir/http3.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/http3.listeners.yaml @@ -47,6 +47,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true name: envoy-gateway/gateway-1/tls-quic udpListenerConfig: downstreamSocketConfig: {} @@ -96,5 +98,7 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true name: envoy-gateway/gateway-1/tls perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-with-jsonpath.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-with-jsonpath.listeners.yaml index 6fe14b03f97..fb7bceafa22 100644 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch-with-jsonpath.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch-with-jsonpath.listeners.yaml @@ -59,5 +59,7 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true name: first-listener perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/jsonpatch.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/jsonpatch.listeners.yaml index 6fe14b03f97..fb7bceafa22 100644 --- a/internal/xds/translator/testdata/out/xds-ir/jsonpatch.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/jsonpatch.listeners.yaml @@ -59,5 +59,7 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true name: first-listener perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.listeners.yaml index 89d57c725f0..210069d7019 100644 --- a/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/listener-proxy-protocol.listeners.yaml @@ -50,6 +50,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true listenerFilters: - name: envoy.filters.listener.proxy_protocol typedConfig: diff --git a/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.listeners.yaml index b7967da1cc0..1426aeaa71f 100644 --- a/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/mixed-tls-jwt-authn.listeners.yaml @@ -43,5 +43,7 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true name: first-listener perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.listeners.yaml index 1e62e5ca424..86002534182 100644 --- a/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port.listeners.yaml @@ -74,6 +74,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true - filterChainMatch: serverNames: - foo.net @@ -117,6 +119,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true - filterChainMatch: serverNames: - bar.com diff --git a/internal/xds/translator/testdata/out/xds-ir/mutual-tls-forward-client-certificate-with-custom-data.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-forward-client-certificate-with-custom-data.listeners.yaml index baaca0e4fb6..7d177b2e092 100644 --- a/internal/xds/translator/testdata/out/xds-ir/mutual-tls-forward-client-certificate-with-custom-data.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-forward-client-certificate-with-custom-data.listeners.yaml @@ -52,6 +52,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: false name: first-listener perConnectionBufferLimitBytes: 32768 @@ -110,6 +112,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: false name: second-listener perConnectionBufferLimitBytes: 32768 @@ -170,6 +174,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: false name: third-listener perConnectionBufferLimitBytes: 32768 @@ -232,6 +238,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: false name: fourth-listener perConnectionBufferLimitBytes: 32768 @@ -296,6 +304,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: false name: fifth-listener perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/mutual-tls-forward-client-certificate.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-forward-client-certificate.listeners.yaml index a99d3fd3906..33262561948 100644 --- a/internal/xds/translator/testdata/out/xds-ir/mutual-tls-forward-client-certificate.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-forward-client-certificate.listeners.yaml @@ -52,6 +52,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: false name: first-listener perConnectionBufferLimitBytes: 32768 @@ -110,6 +112,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: false name: second-listener perConnectionBufferLimitBytes: 32768 @@ -168,6 +172,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: false name: third-listener perConnectionBufferLimitBytes: 32768 @@ -226,6 +232,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: false name: fourth-listener perConnectionBufferLimitBytes: 32768 @@ -284,6 +292,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: false name: fifth-listener perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/mutual-tls-required-client-certificate-disabled.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-required-client-certificate-disabled.listeners.yaml index 28836be314c..e945b5d9221 100644 --- a/internal/xds/translator/testdata/out/xds-ir/mutual-tls-required-client-certificate-disabled.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/mutual-tls-required-client-certificate-disabled.listeners.yaml @@ -52,6 +52,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: false name: first-listener perConnectionBufferLimitBytes: 32768 @@ -85,6 +87,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: false name: second-listener perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/mutual-tls.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/mutual-tls.listeners.yaml index 490cfa0f9d9..fc572910c06 100644 --- a/internal/xds/translator/testdata/out/xds-ir/mutual-tls.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/mutual-tls.listeners.yaml @@ -52,6 +52,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: true name: first-listener perConnectionBufferLimitBytes: 32768 @@ -85,6 +87,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true requireClientCertificate: true name: second-listener perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/simple-tls.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/simple-tls.listeners.yaml index 54f30d4c445..3d65ed1a895 100644 --- a/internal/xds/translator/testdata/out/xds-ir/simple-tls.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/simple-tls.listeners.yaml @@ -47,5 +47,7 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true name: first-listener perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/suppress-envoy-headers.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/suppress-envoy-headers.listeners.yaml index bcc936863ca..4c624b8788f 100644 --- a/internal/xds/translator/testdata/out/xds-ir/suppress-envoy-headers.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/suppress-envoy-headers.listeners.yaml @@ -49,6 +49,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true listenerFilters: - name: envoy.filters.listener.tls_inspector typedConfig: diff --git a/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml index f27dc1b2123..10df3db202c 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tcp-route-tls-terminate.listeners.yaml @@ -23,6 +23,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true - filterChainMatch: serverNames: - '*.envoyproxy.io' @@ -47,6 +49,8 @@ sdsConfig: ads: {} resourceApiVersion: V3 + disableStatefulSessionResumption: true + disableStatelessSessionResumption: true listenerFilters: - name: envoy.filters.listener.tls_inspector typedConfig: diff --git a/internal/xds/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.listeners.yaml index dd12bc2b988..7eee7e167c0 100644 --- a/internal/xds/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.listeners.yaml +++ b/internal/xds/translator/testdata/out/xds-ir/tls-with-ciphers-versions-alpn.listeners.yaml @@ -70,6 +70,7 @@ - rsa_pkcs1_sha1 tlsMaximumProtocolVersion: TLSv1_2 tlsMinimumProtocolVersion: TLSv1_0 + disableStatelessSessionResumption: true listenerFilters: - name: envoy.filters.listener.tls_inspector typedConfig: @@ -121,5 +122,6 @@ - rsa_pkcs1_sha1 tlsMaximumProtocolVersion: TLSv1_2 tlsMinimumProtocolVersion: TLSv1_0 + disableStatefulSessionResumption: true name: second-listener perConnectionBufferLimitBytes: 32768 diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 76adfb15735..956b3dbd228 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -610,6 +610,7 @@ _Appears in:_ | `ecdhCurves` | _string array_ | false | ECDHCurves specifies the set of supported ECDH curves.
In non-FIPS Envoy Proxy builds the default curves are:
- X25519
- P-256
In builds using BoringSSL FIPS the default curve is:
- P-256 | | `signatureAlgorithms` | _string array_ | false | SignatureAlgorithms specifies which signature algorithms the listener should
support. | | `alpnProtocols` | _[ALPNProtocol](#alpnprotocol) array_ | false | ALPNProtocols supplies the list of ALPN protocols that should be
exposed by the listener. By default h2 and http/1.1 are enabled.
Supported values are:
- http/1.0
- http/1.1
- h2 | +| `session` | _[Session](#session)_ | false | Session defines settings related to TLS session management. | #### ClientTimeout @@ -3676,6 +3677,35 @@ _Appears in:_ | `NodePort` | ServiceTypeNodePort means a service will be exposed on each Kubernetes Node
at a static Port, common across all Nodes.
| +#### Session + + + +Session defines settings related to TLS session management. + +_Appears in:_ +- [ClientTLSSettings](#clienttlssettings) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `resumption` | _[SessionResumption](#sessionresumption)_ | false | Resumption determines the proxy's supported TLS session resumption option.
By default, Envoy Gateway does not enable session resumption. Use sessionResumption to
enable stateful and stateless session resumption. Users should consider security impacts
of different resumption methods. Performance gains from resumption are diminished when
Envoy proxy is deployed with more than one replica. | + + +#### SessionResumption + + + +SessionResumption defines supported tls session resumption methods and their associated configuration. + +_Appears in:_ +- [Session](#session) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `stateless` | _[StatelessTLSSessionResumption](#statelesstlssessionresumption)_ | false | Stateless defines setting for stateless (session-ticket based) session resumption | +| `stateful` | _[StatefulTLSSessionResumption](#statefultlssessionresumption)_ | false | Stateful defines setting for stateful (session-id based) session resumption | + + #### ShutdownConfig @@ -3736,6 +3766,37 @@ _Appears in:_ | `Distinct` | SourceMatchDistinct Each IP Address within the specified Source IP CIDR is treated as a distinct client selector
and uses a separate rate limit bucket/counter.
Note: This is only supported for Global Rate Limits.
| +#### StatefulTLSSessionResumption + + + +StatefulTLSSessionResumption defines the stateful (session-id based) type of TLS session resumption. +Note: When Envoy Proxy is deployed with more than one replica, session caches are not synchronized +between instances, possibly leading to resumption failures. +Envoy does not re-validate client certificates upon session resumption. +https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#config-route-v3-routematch-tlscontextmatchoptions + +_Appears in:_ +- [SessionResumption](#sessionresumption) + + + +#### StatelessTLSSessionResumption + + + +StatelessTLSSessionResumption defines the stateless (session-ticket based) type of TLS session resumption. +Note: When Envoy Proxy is deployed with more than one replica, session ticket encryption keys are not +synchronized between instances, possibly leading to resumption failures. +In-memory session ticket encryption keys are rotated every 48 hours. +https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/transport_sockets/tls/v3/common.proto#extensions-transport-sockets-tls-v3-tlssessionticketkeys +https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#Session-tickets + +_Appears in:_ +- [SessionResumption](#sessionresumption) + + + #### StatusCodeMatch diff --git a/site/content/en/latest/tasks/security/secure-gateways.md b/site/content/en/latest/tasks/security/secure-gateways.md index f0e5c8c2697..2c8d5043812 100644 --- a/site/content/en/latest/tasks/security/secure-gateways.md +++ b/site/content/en/latest/tasks/security/secure-gateways.md @@ -512,8 +512,88 @@ Since the multiple certificates are configured on the same Gateway listener, Env {{% /tab %}} {{< /tabpane >}} +## Customize Gateway TLS Parameters + +In addition to enablement of TLS with Gateway-API, Envoy Gateway supports customizing TLS parameters. +To achieve this, the [ClientTrafficPolicy][] resource can be used to specify TLS parameters. +We will customize the minimum supported TLS version in this example to TLSv1.3. + +{{< tabpane text=true >}} +{{% tab header="Apply from stdin" %}} + +```shell +cat <}} + + +## Testing TLS Parameters + +Attempt to connecting using an unsupported TLS version: + +```shell +curl -v -HHost:www.sample.com --resolve "www.sample.com:8443:127.0.0.1" \ +--cacert sample.com.crt --tlsv1.2 --tls-max 1.2 https://www.sample.com:8443/get -I + +[...] + +* ALPN: curl offers h2,http/1.1 +* (304) (OUT), TLS handshake, Client hello (1): +* LibreSSL/3.3.6: error:1404B42E:SSL routines:ST_CONNECT:tlsv1 alert protocol version +* Closing connection +curl: (35) LibreSSL/3.3.6: error:1404B42E:SSL routines:ST_CONNECT:tlsv1 alert protocol version +``` + +The output shows that the connection fails due to an unsupported TLS protocol version used by the client. Now, connect +to the Gateway without specifying a client version, and note that the connection is established with TLSv1.3. + +```shell +curl -v -HHost:www.sample.com --resolve "www.sample.com:8443:127.0.0.1" \ +--cacert sample.com.crt https://www.sample.com:8443/get -I + +[...] + +* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256 / [blank] / UNDEF +``` + ## Next Steps Checkout the [Developer Guide](../../../contributions/develop) to get involved in the project. [ReferenceGrant]: https://gateway-api.sigs.k8s.io/api-types/referencegrant/ +[ClientTrafficPolicy]: ../../api/extension_types#clienttrafficpolicy \ No newline at end of file diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index 76adfb15735..956b3dbd228 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -610,6 +610,7 @@ _Appears in:_ | `ecdhCurves` | _string array_ | false | ECDHCurves specifies the set of supported ECDH curves.
In non-FIPS Envoy Proxy builds the default curves are:
- X25519
- P-256
In builds using BoringSSL FIPS the default curve is:
- P-256 | | `signatureAlgorithms` | _string array_ | false | SignatureAlgorithms specifies which signature algorithms the listener should
support. | | `alpnProtocols` | _[ALPNProtocol](#alpnprotocol) array_ | false | ALPNProtocols supplies the list of ALPN protocols that should be
exposed by the listener. By default h2 and http/1.1 are enabled.
Supported values are:
- http/1.0
- http/1.1
- h2 | +| `session` | _[Session](#session)_ | false | Session defines settings related to TLS session management. | #### ClientTimeout @@ -3676,6 +3677,35 @@ _Appears in:_ | `NodePort` | ServiceTypeNodePort means a service will be exposed on each Kubernetes Node
at a static Port, common across all Nodes.
| +#### Session + + + +Session defines settings related to TLS session management. + +_Appears in:_ +- [ClientTLSSettings](#clienttlssettings) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `resumption` | _[SessionResumption](#sessionresumption)_ | false | Resumption determines the proxy's supported TLS session resumption option.
By default, Envoy Gateway does not enable session resumption. Use sessionResumption to
enable stateful and stateless session resumption. Users should consider security impacts
of different resumption methods. Performance gains from resumption are diminished when
Envoy proxy is deployed with more than one replica. | + + +#### SessionResumption + + + +SessionResumption defines supported tls session resumption methods and their associated configuration. + +_Appears in:_ +- [Session](#session) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `stateless` | _[StatelessTLSSessionResumption](#statelesstlssessionresumption)_ | false | Stateless defines setting for stateless (session-ticket based) session resumption | +| `stateful` | _[StatefulTLSSessionResumption](#statefultlssessionresumption)_ | false | Stateful defines setting for stateful (session-id based) session resumption | + + #### ShutdownConfig @@ -3736,6 +3766,37 @@ _Appears in:_ | `Distinct` | SourceMatchDistinct Each IP Address within the specified Source IP CIDR is treated as a distinct client selector
and uses a separate rate limit bucket/counter.
Note: This is only supported for Global Rate Limits.
| +#### StatefulTLSSessionResumption + + + +StatefulTLSSessionResumption defines the stateful (session-id based) type of TLS session resumption. +Note: When Envoy Proxy is deployed with more than one replica, session caches are not synchronized +between instances, possibly leading to resumption failures. +Envoy does not re-validate client certificates upon session resumption. +https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#config-route-v3-routematch-tlscontextmatchoptions + +_Appears in:_ +- [SessionResumption](#sessionresumption) + + + +#### StatelessTLSSessionResumption + + + +StatelessTLSSessionResumption defines the stateless (session-ticket based) type of TLS session resumption. +Note: When Envoy Proxy is deployed with more than one replica, session ticket encryption keys are not +synchronized between instances, possibly leading to resumption failures. +In-memory session ticket encryption keys are rotated every 48 hours. +https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/transport_sockets/tls/v3/common.proto#extensions-transport-sockets-tls-v3-tlssessionticketkeys +https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#Session-tickets + +_Appears in:_ +- [SessionResumption](#sessionresumption) + + + #### StatusCodeMatch diff --git a/test/e2e/base/manifests.yaml b/test/e2e/base/manifests.yaml index 3bba21844a0..db9a265cba0 100644 --- a/test/e2e/base/manifests.yaml +++ b/test/e2e/base/manifests.yaml @@ -21,12 +21,12 @@ metadata: spec: gatewayClassName: "{GATEWAY_CLASS_NAME}" listeners: - - name: http - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: Same + - name: http + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway @@ -36,12 +36,12 @@ metadata: spec: gatewayClassName: "{GATEWAY_CLASS_NAME}" listeners: - - name: http - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All + - name: http + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: All --- apiVersion: v1 kind: Service @@ -52,14 +52,14 @@ spec: selector: app: infra-backend-v1 ports: - - protocol: TCP - port: 8080 - name: http11 - targetPort: 3000 - - protocol: TCP - port: 8081 - name: http2 - targetPort: 3001 + - protocol: TCP + port: 8080 + name: http11 + targetPort: 3000 + - protocol: TCP + port: 8081 + name: http2 + targetPort: 3001 --- apiVersion: apps/v1 kind: Deployment @@ -79,23 +79,23 @@ spec: app: infra-backend-v1 spec: containers: - - name: infra-backend-v1 - # From https://github.com/kubernetes-sigs/gateway-api/blob/main/conformance/echo-basic/echo-basic.go - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: SERVICE_NAME - value: infra-backend-v1 - resources: - requests: - cpu: 10m + - name: infra-backend-v1 + # From https://github.com/kubernetes-sigs/gateway-api/blob/main/conformance/echo-basic/echo-basic.go + image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SERVICE_NAME + value: infra-backend-v1 + resources: + requests: + cpu: 10m --- apiVersion: v1 kind: Service @@ -106,9 +106,9 @@ spec: selector: app: infra-backend-v2 ports: - - protocol: TCP - port: 8080 - targetPort: 3000 + - protocol: TCP + port: 8080 + targetPort: 3000 --- apiVersion: apps/v1 kind: Deployment @@ -128,22 +128,22 @@ spec: app: infra-backend-v2 spec: containers: - - name: infra-backend-v2 - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: SERVICE_NAME - value: infra-backend-v2 - resources: - requests: - cpu: 10m + - name: infra-backend-v2 + image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SERVICE_NAME + value: infra-backend-v2 + resources: + requests: + cpu: 10m --- apiVersion: v1 kind: Service @@ -154,9 +154,9 @@ spec: selector: app: infra-backend-v3 ports: - - protocol: TCP - port: 8080 - targetPort: 3000 + - protocol: TCP + port: 8080 + targetPort: 3000 --- apiVersion: apps/v1 kind: Deployment @@ -176,22 +176,22 @@ spec: app: infra-backend-v3 spec: containers: - - name: infra-backend-v3 - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: SERVICE_NAME - value: infra-backend-v3 - resources: - requests: - cpu: 10m + - name: infra-backend-v3 + image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SERVICE_NAME + value: infra-backend-v3 + resources: + requests: + cpu: 10m --- apiVersion: v1 kind: Service @@ -202,9 +202,9 @@ spec: selector: app: tls-backend ports: - - protocol: TCP - port: 443 - targetPort: 8443 + - protocol: TCP + port: 443 + targetPort: 8443 --- apiVersion: apps/v1 kind: Deployment @@ -224,38 +224,38 @@ spec: app: tls-backend spec: containers: - - name: tls-backend - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e - volumeMounts: - - name: secret-volume - mountPath: /etc/secret-volume - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: SERVICE_NAME - value: tls-backend - - name: TLS_SERVER_CERT - value: /etc/secret-volume/crt - - name: TLS_SERVER_PRIVKEY - value: /etc/secret-volume/key - resources: - requests: - cpu: 10m + - name: tls-backend + image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e + volumeMounts: + - name: secret-volume + mountPath: /etc/secret-volume + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SERVICE_NAME + value: tls-backend + - name: TLS_SERVER_CERT + value: /etc/secret-volume/crt + - name: TLS_SERVER_PRIVKEY + value: /etc/secret-volume/key + resources: + requests: + cpu: 10m volumes: - - name: secret-volume - secret: - secretName: tls-passthrough-checks-certificate - items: - - key: tls.crt - path: crt - - key: tls.key - path: key + - name: secret-volume + secret: + secretName: tls-passthrough-checks-certificate + items: + - key: tls.crt + path: crt + - key: tls.key + path: key --- apiVersion: v1 kind: Namespace @@ -273,9 +273,9 @@ spec: selector: app: app-backend-v1 ports: - - protocol: TCP - port: 8080 - targetPort: 3000 + - protocol: TCP + port: 8080 + targetPort: 3000 --- apiVersion: apps/v1 kind: Deployment @@ -295,22 +295,22 @@ spec: app: app-backend-v1 spec: containers: - - name: app-backend-v1 - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: SERVICE_NAME - value: app-backend-v1 - resources: - requests: - cpu: 10m + - name: app-backend-v1 + image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SERVICE_NAME + value: app-backend-v1 + resources: + requests: + cpu: 10m --- apiVersion: v1 kind: Service @@ -321,9 +321,9 @@ spec: selector: app: app-backend-v2 ports: - - protocol: TCP - port: 8080 - targetPort: 3000 + - protocol: TCP + port: 8080 + targetPort: 3000 --- apiVersion: apps/v1 kind: Deployment @@ -343,22 +343,22 @@ spec: app: app-backend-v2 spec: containers: - - name: app-backend-v2 - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: SERVICE_NAME - value: app-backend-v2 - resources: - requests: - cpu: 10m + - name: app-backend-v2 + image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SERVICE_NAME + value: app-backend-v2 + resources: + requests: + cpu: 10m --- apiVersion: v1 kind: Namespace @@ -376,9 +376,9 @@ spec: selector: app: web-backend ports: - - protocol: TCP - port: 8080 - targetPort: 3000 + - protocol: TCP + port: 8080 + targetPort: 3000 --- apiVersion: apps/v1 kind: Deployment @@ -398,22 +398,22 @@ spec: app: web-backend spec: containers: - - name: web-backend - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: SERVICE_NAME - value: web-backend - resources: - requests: - cpu: 10m + - name: web-backend + image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SERVICE_NAME + value: web-backend + resources: + requests: + cpu: 10m --- apiVersion: v1 kind: Namespace @@ -427,55 +427,55 @@ metadata: namespace: gateway-preserve-case-backend data: go.mod: | - module srvr - go 1.22 - require ( - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/klauspost/compress v1.17.0 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.51.0 // indirect - ) + module srvr + go 1.22 + require ( + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/klauspost/compress v1.17.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.51.0 // indirect + ) go.sum: | - github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= - github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= - github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= - github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= - github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= - github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= - github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= - github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= + github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= + github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= + github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= + github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= + github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= + github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= + github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= + github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= main.go: | - package main - import ( - "encoding/json" - "fmt" - "log" - "github.com/valyala/fasthttp" - ) - func HandleFastHTTP(ctx *fasthttp.RequestCtx) { - ctx.QueryArgs().VisitAll(func(key, value []byte) { - if string(key) == "headers" { - ctx.Response.Header.Add(string(value), "PrEsEnT") - } - }) - headers := map[string][]string{} - ctx.Request.Header.VisitAll(func(key, value []byte) { - headers[string(key)] = append(headers[string(key)], string(value)) - }) - if d, err := json.MarshalIndent(headers, "", " "); err != nil { - ctx.Error(fmt.Sprintf("%s", err), fasthttp.StatusBadRequest) - } else { - fmt.Fprintf(ctx, string(d)+"\n") - } - } - func main() { - s := fasthttp.Server{ - Handler: HandleFastHTTP, - DisableHeaderNamesNormalizing: true, - } - log.Printf("Starting on port 8000") - log.Fatal(s.ListenAndServe(":8000")) - } + package main + import ( + "encoding/json" + "fmt" + "log" + "github.com/valyala/fasthttp" + ) + func HandleFastHTTP(ctx *fasthttp.RequestCtx) { + ctx.QueryArgs().VisitAll(func(key, value []byte) { + if string(key) == "headers" { + ctx.Response.Header.Add(string(value), "PrEsEnT") + } + }) + headers := map[string][]string{} + ctx.Request.Header.VisitAll(func(key, value []byte) { + headers[string(key)] = append(headers[string(key)], string(value)) + }) + if d, err := json.MarshalIndent(headers, "", " "); err != nil { + ctx.Error(fmt.Sprintf("%s", err), fasthttp.StatusBadRequest) + } else { + fmt.Fprintf(ctx, string(d)+"\n") + } + } + func main() { + s := fasthttp.Server{ + Handler: HandleFastHTTP, + DisableHeaderNamesNormalizing: true, + } + log.Printf("Starting on port 8000") + log.Fatal(s.ListenAndServe(":8000")) + } --- apiVersion: apps/v1 kind: Deployment @@ -493,21 +493,21 @@ spec: app: golang-app spec: containers: - - name: golang-app-container - command: - - sh - - "-c" - - "cp -a /app /app-live && cd /app-live && go run . " - image: golang:1.22.3-alpine - ports: - - containerPort: 8000 - volumeMounts: - - name: go-server - mountPath: /app + - name: golang-app-container + command: + - sh + - "-c" + - "cp -a /app /app-live && cd /app-live && go run . " + image: golang:1.22.3-alpine + ports: + - containerPort: 8000 + volumeMounts: + - name: go-server + mountPath: /app volumes: - - name: go-server - configMap: - name: go-server + - name: go-server + configMap: + name: go-server --- apiVersion: v1 kind: Service @@ -518,9 +518,9 @@ spec: selector: app: golang-app ports: - - protocol: TCP - port: 8000 - targetPort: 8000 + - protocol: TCP + port: 8000 + targetPort: 8000 --- apiVersion: v1 data: @@ -579,9 +579,9 @@ spec: selector: app: tls-backend-2 ports: - - protocol: TCP - port: 443 - targetPort: 8443 + - protocol: TCP + port: 443 + targetPort: 8443 --- apiVersion: apps/v1 kind: Deployment @@ -601,38 +601,38 @@ spec: app: tls-backend-2 spec: containers: - - name: tls-backend - image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e - volumeMounts: - - name: secret-volume - mountPath: /etc/secret-volume - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: SERVICE_NAME - value: tls-backend-2 - - name: TLS_SERVER_CERT - value: /etc/secret-volume/crt - - name: TLS_SERVER_PRIVKEY - value: /etc/secret-volume/key - resources: - requests: - cpu: 10m + - name: tls-backend + image: gcr.io/k8s-staging-gateway-api/echo-basic:v20231214-v1.0.0-140-gf544a46e + volumeMounts: + - name: secret-volume + mountPath: /etc/secret-volume + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: SERVICE_NAME + value: tls-backend-2 + - name: TLS_SERVER_CERT + value: /etc/secret-volume/crt + - name: TLS_SERVER_PRIVKEY + value: /etc/secret-volume/key + resources: + requests: + cpu: 10m volumes: - - name: secret-volume - secret: - secretName: backend-tls-checks-certificate - items: - - key: tls.crt - path: crt - - key: tls.key - path: key + - name: secret-volume + secret: + secretName: backend-tls-checks-certificate + items: + - key: tls.crt + path: crt + - key: tls.key + path: key --- apiVersion: v1 data: @@ -657,12 +657,12 @@ spec: app: envoy-als type: LoadBalancer ports: - - name: grpc-als - protocol: TCP - appProtocol: grpc - port: 8080 - targetPort: 8080 - - name: http-monitoring - protocol: TCP - port: 19001 - targetPort: 19001 + - name: grpc-als + protocol: TCP + appProtocol: grpc + port: 8080 + targetPort: 8080 + - name: http-monitoring + protocol: TCP + port: 19001 + targetPort: 19001 diff --git a/test/e2e/testdata/client-mtls.yaml b/test/e2e/testdata/client-mtls.yaml index fc1a62f1ca4..a34a00f71d1 100644 --- a/test/e2e/testdata/client-mtls.yaml +++ b/test/e2e/testdata/client-mtls.yaml @@ -17,6 +17,16 @@ metadata: spec: gatewayClassName: "{GATEWAY_CLASS_NAME}" listeners: + - name: tls-settings + port: 443 + protocol: HTTPS + hostname: tls-settings.example.com + tls: + certificateRefs: + - group: "" + kind: Secret + name: client-tls-settings-certificate + mode: Terminate - name: mtls port: 443 protocol: HTTPS @@ -69,3 +79,71 @@ spec: - kind: "Secret" group: "" name: "client-mtls-certificate" +--- +# openssl req -x509 -sha256 -nodes -days 3650 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout ca.key -out ca.crt +# cat > openssl.conf < Date: Tue, 15 Oct 2024 12:11:18 -0700 Subject: [PATCH 17/56] build(deps): bump actions/checkout from 4.2.0 to 4.2.1 (#4442) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.0 to 4.2.1. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/d632683dd7b4114ad314bca15554477dd762a938...eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build_and_test.yaml | 18 +++++++++--------- .github/workflows/codeql.yml | 2 +- .github/workflows/docs.yaml | 4 ++-- .../workflows/experimental_conformance.yaml | 2 +- .github/workflows/latest_release.yaml | 4 ++-- .github/workflows/license-scan.yml | 2 +- .github/workflows/release.yaml | 4 ++-- .github/workflows/scorecard.yml | 2 +- .github/workflows/trivy.yml | 2 +- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml index dbbdbcd04ae..6870c1738d2 100644 --- a/.github/workflows/build_and_test.yaml +++ b/.github/workflows/build_and_test.yaml @@ -20,7 +20,7 @@ jobs: lint: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps # Generate the installation manifests first, so it can check # for errors while running `make -k lint` @@ -31,14 +31,14 @@ jobs: gen-check: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps - run: make -k gen-check license-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps - run: make -k licensecheck @@ -48,7 +48,7 @@ jobs: contents: read # for actions/checkout id-token: write # for fetching OIDC token steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps # test @@ -67,7 +67,7 @@ jobs: runs-on: ubuntu-latest needs: [lint, gen-check, license-check, coverage-test] steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps - name: Build EG Multiarch Binaries @@ -87,7 +87,7 @@ jobs: matrix: version: [ v1.28.13, v1.29.8, v1.30.4, v1.31.0 ] steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps - name: Download EG Binaries @@ -116,7 +116,7 @@ jobs: matrix: version: [ v1.28.13, v1.29.8, v1.30.4, v1.31.0 ] steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps - name: Download EG Binaries @@ -143,7 +143,7 @@ jobs: if: ${{ ! startsWith(github.event_name, 'push') }} needs: [build] steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps - name: Setup Graphviz @@ -170,7 +170,7 @@ jobs: runs-on: ubuntu-latest needs: [conformance-test, e2e-test] steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps - name: Download EG Binaries diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 95262e597e5..7e911a0e584 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps - name: Initialize CodeQL diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 57a8868ff7a..687c824ea41 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Check out code - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: ref: ${{ github.event.pull_request.head.sha }} @@ -48,7 +48,7 @@ jobs: contents: write steps: - name: Git checkout - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: submodules: true ref: ${{ github.event.pull_request.head.sha }} diff --git a/.github/workflows/experimental_conformance.yaml b/.github/workflows/experimental_conformance.yaml index 281bdbca9ae..4ca84797797 100644 --- a/.github/workflows/experimental_conformance.yaml +++ b/.github/workflows/experimental_conformance.yaml @@ -21,7 +21,7 @@ jobs: matrix: version: [ v1.28.13, v1.29.8, v1.30.4, v1.31.0 ] steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps # gateway api experimental conformance diff --git a/.github/workflows/latest_release.yaml b/.github/workflows/latest_release.yaml index 875e0a508e5..b280daacadc 100644 --- a/.github/workflows/latest_release.yaml +++ b/.github/workflows/latest_release.yaml @@ -22,7 +22,7 @@ jobs: benchmark-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps - name: Setup Graphviz @@ -57,7 +57,7 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps - name: Generate Release Manifests diff --git a/.github/workflows/license-scan.yml b/.github/workflows/license-scan.yml index ff3f8d31a02..2bbb36ce830 100644 --- a/.github/workflows/license-scan.yml +++ b/.github/workflows/license-scan.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout code - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Run scanner uses: google/osv-scanner-action/osv-scanner-action@19ec1116569a47416e11a45848722b1af31a857b # v1.9.0 with: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 48de6eb9489..a794eb2a1f1 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -15,7 +15,7 @@ jobs: benchmark-test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - uses: ./tools/github-actions/setup-deps - name: Setup Graphviz @@ -50,7 +50,7 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Extract Release Tag and Commit SHA id: vars diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 88324734fb7..44331f3a595 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -21,7 +21,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 with: persist-credentials: false diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 21c50d56902..5c598890a8f 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout code - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 - name: Build an image from Dockerfile run: | From 96f241df280bb897d79a9988e177d99a6d658aae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:22:47 -0700 Subject: [PATCH 18/56] build(deps): bump aquasecurity/trivy-action from 0.24.0 to 0.27.0 (#4443) Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.24.0 to 0.27.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8...5681af892cd0f4997658e2bacc62bd0a894cf564) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/trivy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 5c598890a8f..9e4a1460f8e 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -25,7 +25,7 @@ jobs: IMAGE=envoy-proxy/gateway-dev TAG=${{ github.sha }} make image - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@6e7b7d1fd3e4fef0c5fa8cce1229c54b2c9bd0d8 # v0.24.0 + uses: aquasecurity/trivy-action@5681af892cd0f4997658e2bacc62bd0a894cf564 # v0.27.0 with: image-ref: envoy-proxy/gateway-dev:${{ github.sha }} exit-code: '1' From 3d7552a9e68ea494c2ee8f40f00dd4b188ac1316 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:23:14 -0700 Subject: [PATCH 19/56] build(deps): bump github/codeql-action from 3.26.11 to 3.26.12 (#4444) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.11 to 3.26.12. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea...c36620d31ac7c881962c3d9dd939c40ec9434f2b) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 6 +++--- .github/workflows/scorecard.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 7e911a0e584..0f4771a3f5e 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -36,14 +36,14 @@ jobs: - uses: ./tools/github-actions/setup-deps - name: Initialize CodeQL - uses: github/codeql-action/init@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11 + uses: github/codeql-action/init@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11 + uses: github/codeql-action/autobuild@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11 + uses: github/codeql-action/analyze@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 44331f3a595..5750a0ce3c7 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -40,6 +40,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea # v3.26.11 + uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 with: sarif_file: results.sarif From 172a73a1ebee1415715b51042757d9eef687b701 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 12:23:51 -0700 Subject: [PATCH 20/56] build(deps): bump actions/upload-artifact from 4.4.0 to 4.4.3 (#4441) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.0 to 4.4.3. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/50769540e7f4bd5e21e526ee35c689e35e0d6874...b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build_and_test.yaml | 2 +- .github/workflows/experimental_conformance.yaml | 2 +- .github/workflows/latest_release.yaml | 2 +- .github/workflows/release.yaml | 2 +- .github/workflows/scorecard.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml index 6870c1738d2..80992fc9d6d 100644 --- a/.github/workflows/build_and_test.yaml +++ b/.github/workflows/build_and_test.yaml @@ -74,7 +74,7 @@ jobs: run: make build-multiarch PLATFORMS="linux_amd64 linux_arm64" - name: Upload EG Binaries - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: envoy-gateway path: bin/ diff --git a/.github/workflows/experimental_conformance.yaml b/.github/workflows/experimental_conformance.yaml index 4ca84797797..931831b2bf1 100644 --- a/.github/workflows/experimental_conformance.yaml +++ b/.github/workflows/experimental_conformance.yaml @@ -33,7 +33,7 @@ jobs: run: make experimental-conformance - name: Upload Conformance Report - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: conformance-report-k8s-${{ matrix.version }} path: ./test/conformance/conformance-report-k8s-${{ matrix.version }}.yaml diff --git a/.github/workflows/latest_release.yaml b/.github/workflows/latest_release.yaml index b280daacadc..a0ceb53e08d 100644 --- a/.github/workflows/latest_release.yaml +++ b/.github/workflows/latest_release.yaml @@ -46,7 +46,7 @@ jobs: run: cd test/benchmark && zip -r benchmark_report.zip benchmark_report - name: Upload Benchmark Report - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: benchmark_report path: test/benchmark/benchmark_report.zip diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a794eb2a1f1..a95f411890d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -39,7 +39,7 @@ jobs: run: cd test/benchmark && zip -r benchmark_report.zip benchmark_report - name: Upload Benchmark Report - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: benchmark_report path: test/benchmark/benchmark_report.zip diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 5750a0ce3c7..987f7c2b62d 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -33,7 +33,7 @@ jobs: publish_results: true - name: "Upload artifact" - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 with: name: SARIF file path: results.sarif From 8fc4ecba40b53a21a7854bd7e750c2ab48ef1e1b Mon Sep 17 00:00:00 2001 From: Isaac <10012479+jukie@users.noreply.github.com> Date: Tue, 15 Oct 2024 13:59:07 -0600 Subject: [PATCH 21/56] feat: allow running EnvoyProxy as DaemonSet (#4429) * Update status when running in daemonset mode * Fix helm permissions and fully implement daemonset Signed-off-by: jukie <10012479+Jukie@users.noreply.github.com> --- charts/gateway-helm/templates/_rbac.tpl | 1 + internal/gatewayapi/status/gateway.go | 45 +++++++---- internal/provider/kubernetes/controller.go | 28 ++++++- internal/provider/kubernetes/kubernetes.go | 2 +- internal/provider/kubernetes/predicates.go | 63 +++++++++------ .../provider/kubernetes/predicates_test.go | 80 +++++++++++++------ internal/provider/kubernetes/status.go | 6 +- internal/provider/kubernetes/test/utils.go | 31 ++++++- .../certjen-custom-scheduling.out.yaml | 1 + .../control-plane-with-pdb.out.yaml | 1 + .../helm/gateway-helm/default-config.out.yaml | 1 + .../deployment-custom-topology.out.yaml | 1 + .../deployment-images-config.out.yaml | 1 + .../deployment-priorityclass.out.yaml | 1 + .../envoy-gateway-config.out.yaml | 1 + .../global-images-config.out.yaml | 1 + .../gateway-helm/service-annotations.out.yaml | 1 + 17 files changed, 189 insertions(+), 76 deletions(-) diff --git a/charts/gateway-helm/templates/_rbac.tpl b/charts/gateway-helm/templates/_rbac.tpl index 27e90061b0c..52a5648818c 100644 --- a/charts/gateway-helm/templates/_rbac.tpl +++ b/charts/gateway-helm/templates/_rbac.tpl @@ -43,6 +43,7 @@ apiGroups: - apps resources: - deployments +- daemonsets verbs: - get - list diff --git a/internal/gatewayapi/status/gateway.go b/internal/gatewayapi/status/gateway.go index f891f8c40af..8bf822479d0 100644 --- a/internal/gatewayapi/status/gateway.go +++ b/internal/gatewayapi/status/gateway.go @@ -13,6 +13,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) @@ -30,8 +31,8 @@ func UpdateGatewayStatusAcceptedCondition(gw *gwapiv1.Gateway, accepted bool) *g // UpdateGatewayStatusProgrammedCondition updates the status addresses for the provided gateway // based on the status IP/Hostname of svc and updates the Programmed condition based on the -// service and deployment state. -func UpdateGatewayStatusProgrammedCondition(gw *gwapiv1.Gateway, svc *corev1.Service, deployment *appsv1.Deployment, nodeAddresses ...string) { +// service and deployment or daemonset state. +func UpdateGatewayStatusProgrammedCondition(gw *gwapiv1.Gateway, svc *corev1.Service, envoyObj client.Object, nodeAddresses ...string) { var addresses, hostnames []string // Update the status addresses field. if svc != nil { @@ -98,7 +99,7 @@ func UpdateGatewayStatusProgrammedCondition(gw *gwapiv1.Gateway, svc *corev1.Ser } // Update the programmed condition. - updateGatewayProgrammedCondition(gw, deployment) + updateGatewayProgrammedCondition(gw, envoyObj) } func SetGatewayListenerStatusCondition(gateway *gwapiv1.Gateway, listenerStatusIdx int, @@ -132,13 +133,13 @@ func computeGatewayAcceptedCondition(gw *gwapiv1.Gateway, accepted bool) metav1. const ( messageAddressNotAssigned = "No addresses have been assigned to the Gateway" messageFmtTooManyAddresses = "Too many addresses (%d) have been assigned to the Gateway, the maximum number of addresses is 16" - messageNoResources = "Deployment replicas unavailable" - messageFmtProgrammed = "Address assigned to the Gateway, %d/%d envoy Deployment replicas available" + messageNoResources = "Envoy replicas unavailable" + messageFmtProgrammed = "Address assigned to the Gateway, %d/%d envoy replicas available" ) // updateGatewayProgrammedCondition computes the Gateway Programmed status condition. -// Programmed condition surfaces true when the Envoy Deployment status is ready. -func updateGatewayProgrammedCondition(gw *gwapiv1.Gateway, deployment *appsv1.Deployment) { +// Programmed condition surfaces true when the Envoy Deployment or DaemonSet status is ready. +func updateGatewayProgrammedCondition(gw *gwapiv1.Gateway, envoyObj client.Object) { if len(gw.Status.Addresses) == 0 { gw.Status.Conditions = MergeConditions(gw.Status.Conditions, newCondition(string(gwapiv1.GatewayConditionProgrammed), metav1.ConditionFalse, string(gwapiv1.GatewayReasonAddressNotAssigned), @@ -157,17 +158,27 @@ func updateGatewayProgrammedCondition(gw *gwapiv1.Gateway, deployment *appsv1.De return } - // If there are no available replicas for the Envoy Deployment, don't - // mark the Gateway as ready yet. - - if deployment == nil || deployment.Status.AvailableReplicas == 0 { - gw.Status.Conditions = MergeConditions(gw.Status.Conditions, - newCondition(string(gwapiv1.GatewayConditionProgrammed), metav1.ConditionFalse, string(gwapiv1.GatewayReasonNoResources), - messageNoResources, time.Now(), gw.Generation)) - return + // Check for available Envoy replicas and if found mark the gateway as ready. + switch obj := envoyObj.(type) { + case *appsv1.Deployment: + if obj != nil && obj.Status.AvailableReplicas > 0 { + gw.Status.Conditions = MergeConditions(gw.Status.Conditions, + newCondition(string(gwapiv1.GatewayConditionProgrammed), metav1.ConditionTrue, string(gwapiv1.GatewayConditionProgrammed), + fmt.Sprintf(messageFmtProgrammed, obj.Status.AvailableReplicas, obj.Status.Replicas), time.Now(), gw.Generation)) + return + } + case *appsv1.DaemonSet: + if obj != nil && obj.Status.NumberAvailable > 0 { + gw.Status.Conditions = MergeConditions(gw.Status.Conditions, + newCondition(string(gwapiv1.GatewayConditionProgrammed), metav1.ConditionTrue, string(gwapiv1.GatewayConditionProgrammed), + fmt.Sprintf(messageFmtProgrammed, obj.Status.NumberAvailable, obj.Status.CurrentNumberScheduled), time.Now(), gw.Generation)) + return + } } + // If there are no available replicas for the Envoy Deployment or + // Envoy DaemonSet, don't mark the Gateway as ready yet. gw.Status.Conditions = MergeConditions(gw.Status.Conditions, - newCondition(string(gwapiv1.GatewayConditionProgrammed), metav1.ConditionTrue, string(gwapiv1.GatewayConditionProgrammed), - fmt.Sprintf(messageFmtProgrammed, deployment.Status.AvailableReplicas, deployment.Status.Replicas), time.Now(), gw.Generation)) + newCondition(string(gwapiv1.GatewayConditionProgrammed), metav1.ConditionFalse, string(gwapiv1.GatewayReasonNoResources), + messageNoResources, time.Now(), gw.Generation)) } diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index dac8f1780a8..915e6e5acd8 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -1386,13 +1386,13 @@ func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.M } // Watch Deployment CRUDs and process affected Gateways. - dPredicates := []predicate.TypedPredicate[*appsv1.Deployment]{ + deploymentPredicates := []predicate.TypedPredicate[*appsv1.Deployment]{ predicate.NewTypedPredicateFuncs[*appsv1.Deployment](func(deploy *appsv1.Deployment) bool { - return r.validateDeploymentForReconcile(deploy) + return r.validateObjectForReconcile(deploy) }), } if r.namespaceLabel != nil { - dPredicates = append(dPredicates, predicate.NewTypedPredicateFuncs[*appsv1.Deployment](func(deploy *appsv1.Deployment) bool { + deploymentPredicates = append(deploymentPredicates, predicate.NewTypedPredicateFuncs[*appsv1.Deployment](func(deploy *appsv1.Deployment) bool { return r.hasMatchingNamespaceLabels(deploy) })) } @@ -1401,7 +1401,27 @@ func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.M handler.TypedEnqueueRequestsFromMapFunc(func(ctx context.Context, deploy *appsv1.Deployment) []reconcile.Request { return r.enqueueClass(ctx, deploy) }), - dPredicates...)); err != nil { + deploymentPredicates...)); err != nil { + return err + } + + // Watch DaemonSet CRUDs and process affected Gateways. + daemonsetPredicates := []predicate.TypedPredicate[*appsv1.DaemonSet]{ + predicate.NewTypedPredicateFuncs[*appsv1.DaemonSet](func(daemonset *appsv1.DaemonSet) bool { + return r.validateObjectForReconcile(daemonset) + }), + } + if r.namespaceLabel != nil { + daemonsetPredicates = append(daemonsetPredicates, predicate.NewTypedPredicateFuncs[*appsv1.DaemonSet](func(daemonset *appsv1.DaemonSet) bool { + return r.hasMatchingNamespaceLabels(daemonset) + })) + } + if err := c.Watch( + source.Kind(mgr.GetCache(), &appsv1.DaemonSet{}, + handler.TypedEnqueueRequestsFromMapFunc(func(ctx context.Context, daemonset *appsv1.DaemonSet) []reconcile.Request { + return r.enqueueClass(ctx, daemonset) + }), + daemonsetPredicates...)); err != nil { return err } diff --git a/internal/provider/kubernetes/kubernetes.go b/internal/provider/kubernetes/kubernetes.go index b909eced608..ffef819ee07 100644 --- a/internal/provider/kubernetes/kubernetes.go +++ b/internal/provider/kubernetes/kubernetes.go @@ -107,7 +107,7 @@ func New(cfg *rest.Config, svr *ec.Server, resources *message.ProviderResources) return nil, fmt.Errorf("unable to set up ready check: %w", err) } - // Emit elected & continue with deployment of infra resources + // Emit elected & continue with envoyObjects of infra resources go func() { <-mgr.Elected() close(svr.Elected) diff --git a/internal/provider/kubernetes/predicates.go b/internal/provider/kubernetes/predicates.go index 9fb3fe86fd1..9c4d582b58b 100644 --- a/internal/provider/kubernetes/predicates.go +++ b/internal/provider/kubernetes/predicates.go @@ -13,6 +13,7 @@ import ( corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" @@ -439,21 +440,16 @@ func (r *gatewayAPIReconciler) validateEndpointSliceForReconcile(obj client.Obje return r.isEnvoyExtensionPolicyReferencingBackend(&nsName) } -// validateDeploymentForReconcile tries finding the owning Gateway of the Deployment +// validateObjectForReconcile tries finding the owning Gateway of the Deployment or DaemonSet // if it exists, finds the Gateway's Service, and further updates the Gateway -// status Ready condition. No Deployments are pushed for reconciliation. -func (r *gatewayAPIReconciler) validateDeploymentForReconcile(obj client.Object) bool { +// status Ready condition. No Deployments or DaemonSets are pushed for reconciliation. +func (r *gatewayAPIReconciler) validateObjectForReconcile(obj client.Object) bool { ctx := context.Background() - deployment, ok := obj.(*appsv1.Deployment) - if !ok { - r.log.Info("unexpected object type, bypassing reconciliation", "object", obj) - return false - } - labels := deployment.GetLabels() + labels := obj.GetLabels() - // Only deployments in the configured namespace should be reconciled. - if deployment.Namespace == r.namespace { - // Check if the deployment belongs to a Gateway, if so, update the Gateway status. + // Only objects in the configured namespace should be reconciled. + if obj.GetNamespace() == r.namespace { + // Check if the obj belongs to a Gateway, if so, update the Gateway status. gtw := r.findOwningGateway(ctx, labels) if gtw != nil { r.updateStatusForGateway(ctx, gtw) @@ -471,27 +467,42 @@ func (r *gatewayAPIReconciler) validateDeploymentForReconcile(obj client.Object) return false } - // There is no need to reconcile the Deployment any further. + // There is no need to reconcile the object any further. return false } -// envoyDeploymentForGateway returns the Envoy Deployment, returning nil if the Deployment doesn't exist. -func (r *gatewayAPIReconciler) envoyDeploymentForGateway(ctx context.Context, gateway *gwapiv1.Gateway) (*appsv1.Deployment, error) { - var deployments appsv1.DeploymentList - labelSelector := labels.SelectorFromSet(labels.Set(gatewayapi.OwnerLabels(gateway, r.mergeGateways.Has(string(gateway.Spec.GatewayClassName))))) - if err := r.client.List(ctx, &deployments, &client.ListOptions{ - LabelSelector: labelSelector, - Namespace: r.namespace, - }); err != nil { - if kerrors.IsNotFound(err) { +// envoyObjectForGateway returns the Envoy Deployment or DaemonSet, returning nil if neither exists. +func (r *gatewayAPIReconciler) envoyObjectForGateway(ctx context.Context, gateway *gwapiv1.Gateway) (client.Object, error) { + // Helper func to list and return the first object from results + listResource := func(list client.ObjectList) (client.Object, error) { + if err := r.client.List(ctx, list, &client.ListOptions{ + LabelSelector: labels.SelectorFromSet(gatewayapi.OwnerLabels(gateway, r.mergeGateways.Has(string(gateway.Spec.GatewayClassName)))), + Namespace: r.namespace, + }); err != nil { + if !kerrors.IsNotFound(err) { + return nil, err + } + } + items, err := meta.ExtractList(list) + if err != nil || len(items) == 0 { return nil, nil } - return nil, err + return items[0].(client.Object), nil } - if len(deployments.Items) == 0 { - return nil, nil + + // Check for Deployment + deployments := &appsv1.DeploymentList{} + if obj, err := listResource(deployments); obj != nil || err != nil { + return obj, err + } + + // Check for DaemonSet + daemonsets := &appsv1.DaemonSetList{} + if obj, err := listResource(daemonsets); obj != nil || err != nil { + return obj, err } - return &deployments.Items[0], nil + + return nil, nil } // envoyServiceForGateway returns the Envoy service, returning nil if the service doesn't exist. diff --git a/internal/provider/kubernetes/predicates_test.go b/internal/provider/kubernetes/predicates_test.go index 61a09ffb8ae..ef8182ffdb9 100644 --- a/internal/provider/kubernetes/predicates_test.go +++ b/internal/provider/kubernetes/predicates_test.go @@ -525,7 +525,7 @@ func TestValidateServiceForReconcile(t *testing.T) { expect bool }{ { - name: "gateway service but deployment does not exist", + name: "gateway service but deployment or daemonset does not exist", configs: []client.Object{ test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil), sampleGateway, @@ -547,7 +547,22 @@ func TestValidateServiceForReconcile(t *testing.T) { gatewayapi.OwningGatewayNameLabel: "scheduled-status-test", gatewayapi.OwningGatewayNamespaceLabel: "default", }, nil), - // Note that in case when a deployment exists, the Service is just processed for Gateway status + // Note that in case when a envoyObjects exists, the Service is just processed for Gateway status + // updates and not reconciled further. + expect: false, + }, + { + name: "gateway service daemonset also exist", + configs: []client.Object{ + test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil), + sampleGateway, + test.GetGatewayDaemonSet(types.NamespacedName{Name: proxy.ExpectedResourceHashedName("default/scheduled-status-test")}, nil), + }, + service: test.GetService(types.NamespacedName{Name: "service"}, map[string]string{ + gatewayapi.OwningGatewayNameLabel: "scheduled-status-test", + gatewayapi.OwningGatewayNamespaceLabel: "default", + }, nil), + // Note that in case when a envoyObjects exists, the Service is just processed for Gateway status // updates and not reconciled further. expect: false, }, @@ -859,34 +874,39 @@ func TestValidateServiceForReconcile(t *testing.T) { } } -// TestValidateDeploymentForReconcile tests the validateDeploymentForReconcile +// TestValidateObjectForReconcile tests the validateObjectForReconcile // predicate function. -func TestValidateDeploymentForReconcile(t *testing.T) { +func TestValidateObjectForReconcile(t *testing.T) { sampleGateway := test.GetGateway(types.NamespacedName{Namespace: "default", Name: "scheduled-status-test"}, "test-gc", 8080) mergeGatewaysConfig := test.GetEnvoyProxy(types.NamespacedName{Namespace: "default", Name: "merge-gateways-config"}, true) testCases := []struct { - name string - configs []client.Object - deployment client.Object - expect bool + name string + configs []client.Object + envoyObjects []client.Object + expect bool }{ { - // No config should lead to a reconciliation of a Deployment object. The main - // purpose of the Deployment watcher is just for update Gateway object statuses. - name: "gateway deployment deployment also exist", + // No config should lead to a reconciliation of a Deployment or DaemonSet object. The main + // purpose of the watcher is just for updating Gateway object statuses. + name: "gateway deployment or daemonset also exist", configs: []client.Object{ test.GetGatewayClass("test-gc", egv1a1.GatewayControllerName, nil), sampleGateway, - test.GetService(types.NamespacedName{Name: "deployment"}, map[string]string{ + test.GetService(types.NamespacedName{Name: "envoyObjects"}, map[string]string{ gatewayapi.OwningGatewayNameLabel: "scheduled-status-test", gatewayapi.OwningGatewayNamespaceLabel: "default", }, nil), }, - deployment: test.GetGatewayDeployment(types.NamespacedName{Name: "deployment"}, map[string]string{ - gatewayapi.OwningGatewayNameLabel: "scheduled-status-test", - gatewayapi.OwningGatewayNamespaceLabel: "default", - }), + envoyObjects: []client.Object{ + test.GetGatewayDeployment(types.NamespacedName{Name: "deployment"}, map[string]string{ + gatewayapi.OwningGatewayNameLabel: "scheduled-status-test", + gatewayapi.OwningGatewayNamespaceLabel: "default", + }), test.GetGatewayDaemonSet(types.NamespacedName{Name: "daemonset"}, map[string]string{ + gatewayapi.OwningGatewayNameLabel: "scheduled-status-test", + gatewayapi.OwningGatewayNamespaceLabel: "default", + }), + }, expect: false, }, { @@ -900,9 +920,14 @@ func TestValidateDeploymentForReconcile(t *testing.T) { }), mergeGatewaysConfig, }, - deployment: test.GetGatewayDeployment(types.NamespacedName{Name: "deployment"}, map[string]string{ - gatewayapi.OwningGatewayClassLabel: "test-mg", - }), + envoyObjects: []client.Object{ + test.GetGatewayDeployment(types.NamespacedName{Name: "deployment"}, map[string]string{ + gatewayapi.OwningGatewayClassLabel: "test-mg", + }), + test.GetGatewayDaemonSet(types.NamespacedName{Name: "daemonset"}, map[string]string{ + gatewayapi.OwningGatewayClassLabel: "test-mg", + }), + }, expect: false, }, { @@ -919,9 +944,14 @@ func TestValidateDeploymentForReconcile(t *testing.T) { test.GetGateway(types.NamespacedName{Name: "merged-gateway-2", Namespace: "default"}, "test-mg", 8082), test.GetGateway(types.NamespacedName{Name: "merged-gateway-3", Namespace: "default"}, "test-mg", 8083), }, - deployment: test.GetGatewayDeployment(types.NamespacedName{Name: "deployment"}, map[string]string{ - gatewayapi.OwningGatewayClassLabel: "test-mg", - }), + envoyObjects: []client.Object{ + test.GetGatewayDeployment(types.NamespacedName{Name: "deployment"}, map[string]string{ + gatewayapi.OwningGatewayClassLabel: "test-mg", + }), + test.GetGatewayDaemonSet(types.NamespacedName{Name: "daemonset"}, map[string]string{ + gatewayapi.OwningGatewayClassLabel: "test-mg", + }), + }, expect: false, }, } @@ -938,8 +968,10 @@ func TestValidateDeploymentForReconcile(t *testing.T) { for _, tc := range testCases { r.client = fakeclient.NewClientBuilder().WithScheme(envoygateway.GetScheme()).WithObjects(tc.configs...).Build() t.Run(tc.name, func(t *testing.T) { - res := r.validateDeploymentForReconcile(tc.deployment) - require.Equal(t, tc.expect, res) + for _, obj := range tc.envoyObjects { + res := r.validateObjectForReconcile(obj) + require.Equal(t, tc.expect, res) + } }) } } diff --git a/internal/provider/kubernetes/status.go b/internal/provider/kubernetes/status.go index c94ad2bc556..c3d5553b0bf 100644 --- a/internal/provider/kubernetes/status.go +++ b/internal/provider/kubernetes/status.go @@ -475,8 +475,8 @@ func (r *gatewayAPIReconciler) updateStatusForGateway(ctx context.Context, gtw * return } - // Get deployment - deploy, err := r.envoyDeploymentForGateway(ctx, gtw) + // Get envoyObjects + envoyObj, err := r.envoyObjectForGateway(ctx, gtw) if err != nil { r.log.Info("failed to get Deployment for gateway", "namespace", gtw.Namespace, "name", gtw.Name) @@ -491,7 +491,7 @@ func (r *gatewayAPIReconciler) updateStatusForGateway(ctx context.Context, gtw * // update accepted condition status.UpdateGatewayStatusAcceptedCondition(gtw, true) // update address field and programmed condition - status.UpdateGatewayStatusProgrammedCondition(gtw, svc, deploy, r.store.listNodeAddresses()...) + status.UpdateGatewayStatusProgrammedCondition(gtw, svc, envoyObj, r.store.listNodeAddresses()...) key := utils.NamespacedName(gtw) diff --git a/internal/provider/kubernetes/test/utils.go b/internal/provider/kubernetes/test/utils.go index 6fe50fa75bd..77bc50c5e6f 100644 --- a/internal/provider/kubernetes/test/utils.go +++ b/internal/provider/kubernetes/test/utils.go @@ -12,6 +12,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" @@ -271,7 +272,7 @@ func GetUDPRoute(nsName types.NamespacedName, parent string, serviceName types.N } // GetGatewayDeployment returns a sample Deployment for a Gateway object. -func GetGatewayDeployment(nsName types.NamespacedName, labels map[string]string) *appsv1.Deployment { +func GetGatewayDeployment(nsName types.NamespacedName, labels map[string]string) client.Object { return &appsv1.Deployment{ ObjectMeta: metav1.ObjectMeta{ Namespace: nsName.Namespace, @@ -298,6 +299,34 @@ func GetGatewayDeployment(nsName types.NamespacedName, labels map[string]string) } } +// GetGatewayDaemonSet returns a sample DaemonSet for a Gateway object. +func GetGatewayDaemonSet(nsName types.NamespacedName, labels map[string]string) client.Object { + return &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: nsName.Namespace, + Name: nsName.Name, + Labels: labels, + }, + Spec: appsv1.DaemonSetSpec{ + Selector: &metav1.LabelSelector{MatchLabels: labels}, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: labels, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: "dummy", + Image: "dummy", + Ports: []corev1.ContainerPort{{ + ContainerPort: 8080, + }}, + }}, + }, + }, + }, + } +} + // GetService returns a sample Service with labels and ports. func GetService(nsName types.NamespacedName, labels map[string]string, ports map[string]int32) *corev1.Service { service := &corev1.Service{ diff --git a/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml b/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml index 096e1eb5561..8a1513469de 100644 --- a/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml +++ b/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml @@ -105,6 +105,7 @@ rules: - apps resources: - deployments + - daemonsets verbs: - get - list diff --git a/test/helm/gateway-helm/control-plane-with-pdb.out.yaml b/test/helm/gateway-helm/control-plane-with-pdb.out.yaml index 4c9a3d6cfdf..a71e46fe7bd 100644 --- a/test/helm/gateway-helm/control-plane-with-pdb.out.yaml +++ b/test/helm/gateway-helm/control-plane-with-pdb.out.yaml @@ -120,6 +120,7 @@ rules: - apps resources: - deployments + - daemonsets verbs: - get - list diff --git a/test/helm/gateway-helm/default-config.out.yaml b/test/helm/gateway-helm/default-config.out.yaml index c830348f012..140d271c6e7 100644 --- a/test/helm/gateway-helm/default-config.out.yaml +++ b/test/helm/gateway-helm/default-config.out.yaml @@ -105,6 +105,7 @@ rules: - apps resources: - deployments + - daemonsets verbs: - get - list diff --git a/test/helm/gateway-helm/deployment-custom-topology.out.yaml b/test/helm/gateway-helm/deployment-custom-topology.out.yaml index fd468b505f0..586b64b5584 100644 --- a/test/helm/gateway-helm/deployment-custom-topology.out.yaml +++ b/test/helm/gateway-helm/deployment-custom-topology.out.yaml @@ -105,6 +105,7 @@ rules: - apps resources: - deployments + - daemonsets verbs: - get - list diff --git a/test/helm/gateway-helm/deployment-images-config.out.yaml b/test/helm/gateway-helm/deployment-images-config.out.yaml index aa5a36ff23d..10f849e1d77 100644 --- a/test/helm/gateway-helm/deployment-images-config.out.yaml +++ b/test/helm/gateway-helm/deployment-images-config.out.yaml @@ -105,6 +105,7 @@ rules: - apps resources: - deployments + - daemonsets verbs: - get - list diff --git a/test/helm/gateway-helm/deployment-priorityclass.out.yaml b/test/helm/gateway-helm/deployment-priorityclass.out.yaml index d3648d443d9..4f735c42095 100644 --- a/test/helm/gateway-helm/deployment-priorityclass.out.yaml +++ b/test/helm/gateway-helm/deployment-priorityclass.out.yaml @@ -105,6 +105,7 @@ rules: - apps resources: - deployments + - daemonsets verbs: - get - list diff --git a/test/helm/gateway-helm/envoy-gateway-config.out.yaml b/test/helm/gateway-helm/envoy-gateway-config.out.yaml index aa91dacecc8..04159958265 100644 --- a/test/helm/gateway-helm/envoy-gateway-config.out.yaml +++ b/test/helm/gateway-helm/envoy-gateway-config.out.yaml @@ -107,6 +107,7 @@ rules: - apps resources: - deployments + - daemonsets verbs: - get - list diff --git a/test/helm/gateway-helm/global-images-config.out.yaml b/test/helm/gateway-helm/global-images-config.out.yaml index e18eecd7bc7..f280fc9f218 100644 --- a/test/helm/gateway-helm/global-images-config.out.yaml +++ b/test/helm/gateway-helm/global-images-config.out.yaml @@ -109,6 +109,7 @@ rules: - apps resources: - deployments + - daemonsets verbs: - get - list diff --git a/test/helm/gateway-helm/service-annotations.out.yaml b/test/helm/gateway-helm/service-annotations.out.yaml index 97f39cd0bea..ec50a16e30d 100644 --- a/test/helm/gateway-helm/service-annotations.out.yaml +++ b/test/helm/gateway-helm/service-annotations.out.yaml @@ -105,6 +105,7 @@ rules: - apps resources: - deployments + - daemonsets verbs: - get - list From 5880d6bc0ae65c923142605e87516bb4e46bc6a8 Mon Sep 17 00:00:00 2001 From: Kensei Nakada Date: Wed, 16 Oct 2024 10:46:01 +1100 Subject: [PATCH 22/56] feat: implement RequestTimeout in BackendTrafficPolicy (#4329) * feat: implement RequestTimeout in BackendTrafficPolicy Signed-off-by: Kensei Nakada * fix: the timeout on HTTPRoute overwrites the timeout on BTP Signed-off-by: Kensei Nakada * fix: regenerate from the latest api spec Signed-off-by: Kensei Nakada --------- Signed-off-by: Kensei Nakada --- api/v1alpha1/timeout_types.go | 5 ++++ api/v1alpha1/zz_generated.deepcopy.go | 5 ++++ ....envoyproxy.io_backendtrafficpolicies.yaml | 5 ++++ ....envoyproxy.io_envoyextensionpolicies.yaml | 5 ++++ .../gateway.envoyproxy.io_envoyproxies.yaml | 26 ++++++++++++++++++ ...ateway.envoyproxy.io_securitypolicies.yaml | 15 +++++++++++ internal/gatewayapi/clustersettings.go | 27 ++++++++++++------- .../backendtrafficpolicy-with-timeout.in.yaml | 4 +++ ...backendtrafficpolicy-with-timeout.out.yaml | 6 +++++ site/content/en/latest/api/extension_types.md | 1 + site/content/zh/latest/api/extension_types.md | 1 + 11 files changed, 91 insertions(+), 9 deletions(-) diff --git a/api/v1alpha1/timeout_types.go b/api/v1alpha1/timeout_types.go index 36c0c320ed2..008582578d1 100644 --- a/api/v1alpha1/timeout_types.go +++ b/api/v1alpha1/timeout_types.go @@ -40,6 +40,11 @@ type HTTPTimeout struct { // // +optional MaxConnectionDuration *gwapiv1.Duration `json:"maxConnectionDuration,omitempty"` + + // RequestTimeout is the time until which entire response is received from the upstream. + // + // +optional + RequestTimeout *gwapiv1.Duration `json:"requestTimeout,omitempty" yaml:"requestTimeout,omitempty"` } type ClientTimeout struct { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 2ce5a5762ee..a5e8dc183ff 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -2845,6 +2845,11 @@ func (in *HTTPTimeout) DeepCopyInto(out *HTTPTimeout) { *out = new(apisv1.Duration) **out = **in } + if in.RequestTimeout != nil { + in, out := &in.RequestTimeout, &out.RequestTimeout + *out = new(apisv1.Duration) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPTimeout. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index 7b13e2a123f..c0dce593804 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -1316,6 +1316,11 @@ spec: Default: unlimited. pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string + requestTimeout: + description: RequestTimeout is the time until which entire + response is received from the upstream. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string type: object tcp: description: Timeout settings for TCP. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml index 867b8933c0c..6baa2842c0c 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyextensionpolicies.yaml @@ -878,6 +878,11 @@ spec: Default: unlimited. pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string + requestTimeout: + description: RequestTimeout is the time until which + entire response is received from the upstream. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string type: object tcp: description: Timeout settings for TCP. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index 487f436ab81..5b0130f2736 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -11220,6 +11220,13 @@ spec: Default: unlimited. pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string + requestTimeout: + description: RequestTimeout is + the time until which entire + response is received from the + upstream. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string type: object tcp: description: Timeout settings for @@ -12190,6 +12197,13 @@ spec: Default: unlimited. pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string + requestTimeout: + description: RequestTimeout is + the time until which entire + response is received from the + upstream. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string type: object tcp: description: Timeout settings for @@ -13220,6 +13234,12 @@ spec: Default: unlimited. pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string + requestTimeout: + description: RequestTimeout is the time + until which entire response is received + from the upstream. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string type: object tcp: description: Timeout settings for TCP. @@ -14195,6 +14215,12 @@ spec: Default: unlimited. pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string + requestTimeout: + description: RequestTimeout is the time until + which entire response is received from the + upstream. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string type: object tcp: description: Timeout settings for TCP. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml index 8c4debcef0a..ad2c81818c2 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -1182,6 +1182,11 @@ spec: Default: unlimited. pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string + requestTimeout: + description: RequestTimeout is the time until + which entire response is received from the upstream. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string type: object tcp: description: Timeout settings for TCP. @@ -2060,6 +2065,11 @@ spec: Default: unlimited. pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string + requestTimeout: + description: RequestTimeout is the time until + which entire response is received from the upstream. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string type: object tcp: description: Timeout settings for TCP. @@ -3214,6 +3224,11 @@ spec: Default: unlimited. pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string + requestTimeout: + description: RequestTimeout is the time until + which entire response is received from the upstream. + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string type: object tcp: description: Timeout settings for TCP. diff --git a/internal/gatewayapi/clustersettings.go b/internal/gatewayapi/clustersettings.go index cf8221d277d..742b026249e 100644 --- a/internal/gatewayapi/clustersettings.go +++ b/internal/gatewayapi/clustersettings.go @@ -81,11 +81,11 @@ func translateTrafficFeatures(policy *egv1a1.ClusterSettings) (*ir.TrafficFeatur return ret, nil } -func buildClusterSettingsTimeout(policy egv1a1.ClusterSettings, traffic *ir.TrafficFeatures) (*ir.Timeout, error) { +func buildClusterSettingsTimeout(policy egv1a1.ClusterSettings, routeTrafficFeatures *ir.TrafficFeatures) (*ir.Timeout, error) { if policy.Timeout == nil { - if traffic != nil { + if routeTrafficFeatures != nil { // Don't lose any existing timeout definitions. - return mergeTimeoutSettings(nil, traffic.Timeout), nil + return mergeTimeoutSettings(nil, routeTrafficFeatures.Timeout), nil } return nil, nil } @@ -109,6 +109,7 @@ func buildClusterSettingsTimeout(policy egv1a1.ClusterSettings, traffic *ir.Traf if pto.HTTP != nil { var cit *metav1.Duration var mcd *metav1.Duration + var rt *metav1.Duration if pto.HTTP.ConnectionIdleTimeout != nil { d, err := time.ParseDuration(string(*pto.HTTP.ConnectionIdleTimeout)) @@ -128,19 +129,27 @@ func buildClusterSettingsTimeout(policy egv1a1.ClusterSettings, traffic *ir.Traf } } + if pto.HTTP.RequestTimeout != nil { + d, err := time.ParseDuration(string(*pto.HTTP.RequestTimeout)) + if err != nil { + errs = errors.Join(errs, fmt.Errorf("invalid RequestTimeout value %s", *pto.HTTP.RequestTimeout)) + } else { + rt = ptr.To(metav1.Duration{Duration: d}) + } + } + to.HTTP = &ir.HTTPTimeout{ ConnectionIdleTimeout: cit, MaxConnectionDuration: mcd, + RequestTimeout: rt, } } - // http request timeout is translated during the gateway-api route resource translation - // merge route timeout setting with backendtrafficpolicy timeout settings. - // Merging is done after the clustersettings definitions are translated so that - // clustersettings will override previous settings. - if traffic != nil { - to = mergeTimeoutSettings(to, traffic.Timeout) + // The timeout from route's TrafficFeatures takes precedence over the timeout in BTP + if routeTrafficFeatures != nil { + to = mergeTimeoutSettings(routeTrafficFeatures.Timeout, to) } + return to, errs } diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.in.yaml index ef8843f70c4..30a9a3133ab 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.in.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.in.yaml @@ -62,6 +62,8 @@ httpRoutes: backendRefs: - name: service-1 port: 8080 + timeouts: + request: 1s backendTrafficPolicies: - apiVersion: gateway.envoyproxy.io/v1alpha1 kind: BackendTrafficPolicy @@ -79,6 +81,7 @@ backendTrafficPolicies: http: connectionIdleTimeout: 16s maxConnectionDuration: 17s + requestTimeout: 18s - apiVersion: gateway.envoyproxy.io/v1alpha1 kind: BackendTrafficPolicy metadata: @@ -95,3 +98,4 @@ backendTrafficPolicies: http: connectionIdleTimeout: 21s maxConnectionDuration: 22s + requestTimeout: 23s diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml index 5213fc9d6a2..0fad514c5e8 100644 --- a/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-timeout.out.yaml @@ -14,6 +14,7 @@ backendTrafficPolicies: http: connectionIdleTimeout: 21s maxConnectionDuration: 22s + requestTimeout: 23s tcp: connectTimeout: 20s status: @@ -46,6 +47,7 @@ backendTrafficPolicies: http: connectionIdleTimeout: 16s maxConnectionDuration: 17s + requestTimeout: 18s tcp: connectTimeout: 15s status: @@ -198,6 +200,8 @@ httpRoutes: matches: - path: value: / + timeouts: + request: 1s status: parents: - conditions: @@ -289,6 +293,7 @@ xdsIR: http: connectionIdleTimeout: 16s maxConnectionDuration: 17s + requestTimeout: 18s tcp: connectTimeout: 15s envoy-gateway/gateway-2: @@ -336,5 +341,6 @@ xdsIR: http: connectionIdleTimeout: 21s maxConnectionDuration: 22s + requestTimeout: 1s tcp: connectTimeout: 20s diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 956b3dbd228..60e92c2e019 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -2071,6 +2071,7 @@ _Appears in:_ | --- | --- | --- | --- | | `connectionIdleTimeout` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ | false | The idle timeout for an HTTP connection. Idle time is defined as a period in which there are no active requests in the connection.
Default: 1 hour. | | `maxConnectionDuration` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ | false | The maximum duration of an HTTP connection.
Default: unlimited. | +| `requestTimeout` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ | false | RequestTimeout is the time until which entire response is received from the upstream. | #### HTTPURLRewriteFilter diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index 956b3dbd228..60e92c2e019 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -2071,6 +2071,7 @@ _Appears in:_ | --- | --- | --- | --- | | `connectionIdleTimeout` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ | false | The idle timeout for an HTTP connection. Idle time is defined as a period in which there are no active requests in the connection.
Default: 1 hour. | | `maxConnectionDuration` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ | false | The maximum duration of an HTTP connection.
Default: unlimited. | +| `requestTimeout` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ | false | RequestTimeout is the time until which entire response is received from the upstream. | #### HTTPURLRewriteFilter From 958df4899d826c03ecd3aafaea379289bba85f3a Mon Sep 17 00:00:00 2001 From: zirain Date: Wed, 16 Oct 2024 14:03:37 +0800 Subject: [PATCH 23/56] infra: use labels when deleting infra (#4430) --- api/v1alpha1/shared_types.go | 2 +- internal/infrastructure/kubernetes/infra.go | 2 + .../kubernetes/infra_resource.go | 43 ++++++++++++++++--- .../kubernetes/proxy/resource_provider.go | 5 +++ .../kubernetes/ratelimit/resource_provider.go | 5 +++ .../kubernetes/ratelimit_infra.go | 4 -- site/content/en/latest/api/extension_types.md | 2 +- site/content/zh/latest/api/extension_types.md | 2 +- 8 files changed, 51 insertions(+), 14 deletions(-) diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go index 3f165cc6c2d..8e98b904e35 100644 --- a/api/v1alpha1/shared_types.go +++ b/api/v1alpha1/shared_types.go @@ -98,7 +98,7 @@ type KubernetesDeploymentSpec struct { // TODO: Expose config as use cases are better understood, e.g. labels. } -// KubernetesDaemonsetSpec defines the desired state of the Kubernetes daemonset resource. +// KubernetesDaemonSetSpec defines the desired state of the Kubernetes daemonset resource. type KubernetesDaemonSetSpec struct { // Patch defines how to perform the patch operation to daemonset // diff --git a/internal/infrastructure/kubernetes/infra.go b/internal/infrastructure/kubernetes/infra.go index fed1f17cbe7..3d9c3e7b14f 100644 --- a/internal/infrastructure/kubernetes/infra.go +++ b/internal/infrastructure/kubernetes/infra.go @@ -13,6 +13,7 @@ import ( autoscalingv2 "k8s.io/api/autoscaling/v2" corev1 "k8s.io/api/core/v1" policyv1 "k8s.io/api/policy/v1" + "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" @@ -29,6 +30,7 @@ var _ ResourceRender = &ratelimit.ResourceRender{} // based on Infra IR resources. type ResourceRender interface { Name() string + LabelSelector() labels.Selector ServiceAccount() (*corev1.ServiceAccount, error) Service() (*corev1.Service, error) ConfigMap() (*corev1.ConfigMap, error) diff --git a/internal/infrastructure/kubernetes/infra_resource.go b/internal/infrastructure/kubernetes/infra_resource.go index 9966f5ebdd4..04d0a2e0c1f 100644 --- a/internal/infrastructure/kubernetes/infra_resource.go +++ b/internal/infrastructure/kubernetes/infra_resource.go @@ -19,6 +19,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" "github.com/envoyproxy/gateway/internal/metrics" ) @@ -395,7 +396,11 @@ func (i *Infra) deleteServiceAccount(ctx context.Context, r ResourceRender) (err } }() - return i.Client.Delete(ctx, sa) + return i.Client.DeleteAllOf(ctx, sa, &client.DeleteAllOfOptions{ + ListOptions: client.ListOptions{ + LabelSelector: r.LabelSelector(), + }, + }) } // deleteDeployment deletes the Envoy Deployment in the kube api server, if it exists. @@ -430,7 +435,11 @@ func (i *Infra) deleteDeployment(ctx context.Context, r ResourceRender) (err err } }() - return i.Client.Delete(ctx, deployment) + return i.Client.DeleteAllOf(ctx, deployment, &client.DeleteAllOfOptions{ + ListOptions: client.ListOptions{ + LabelSelector: r.LabelSelector(), + }, + }) } // deleteDaemonSet deletes the Envoy DaemonSet in the kube api server, if it exists. @@ -465,7 +474,11 @@ func (i *Infra) deleteDaemonSet(ctx context.Context, r ResourceRender) (err erro } }() - return i.Client.Delete(ctx, daemonSet) + return i.Client.DeleteAllOf(ctx, daemonSet, &client.DeleteAllOfOptions{ + ListOptions: client.ListOptions{ + LabelSelector: r.LabelSelector(), + }, + }) } // deleteConfigMap deletes the ConfigMap in the kube api server, if it exists. @@ -495,7 +508,11 @@ func (i *Infra) deleteConfigMap(ctx context.Context, r ResourceRender) (err erro } }() - return i.Client.Delete(ctx, cm) + return i.Client.DeleteAllOf(ctx, cm, &client.DeleteAllOfOptions{ + ListOptions: client.ListOptions{ + LabelSelector: r.LabelSelector(), + }, + }) } // deleteService deletes the Service in the kube api server, if it exists. @@ -525,7 +542,11 @@ func (i *Infra) deleteService(ctx context.Context, r ResourceRender) (err error) } }() - return i.Client.Delete(ctx, svc) + return i.Client.DeleteAllOf(ctx, svc, &client.DeleteAllOfOptions{ + ListOptions: client.ListOptions{ + LabelSelector: r.LabelSelector(), + }, + }) } // deleteHpa deletes the Horizontal Pod Autoscaler associated to its renderer, if it exists. @@ -560,7 +581,11 @@ func (i *Infra) deleteHPA(ctx context.Context, r ResourceRender) (err error) { } }() - return i.Client.Delete(ctx, hpa) + return i.Client.DeleteAllOf(ctx, hpa, &client.DeleteAllOfOptions{ + ListOptions: client.ListOptions{ + LabelSelector: r.LabelSelector(), + }, + }) } // deletePDB deletes the PodDistribution budget associated to its renderer, if it exists. @@ -595,5 +620,9 @@ func (i *Infra) deletePDB(ctx context.Context, r ResourceRender) (err error) { } }() - return i.Client.Delete(ctx, pdb) + return i.Client.DeleteAllOf(ctx, pdb, &client.DeleteAllOfOptions{ + ListOptions: client.ListOptions{ + LabelSelector: r.LabelSelector(), + }, + }) } diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider.go b/internal/infrastructure/kubernetes/proxy/resource_provider.go index 233afddce73..5411c29e1c6 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_provider.go +++ b/internal/infrastructure/kubernetes/proxy/resource_provider.go @@ -15,6 +15,7 @@ import ( corev1 "k8s.io/api/core/v1" policyv1 "k8s.io/api/policy/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" @@ -46,6 +47,10 @@ func (r *ResourceRender) Name() string { return ExpectedResourceHashedName(r.infra.Name) } +func (r *ResourceRender) LabelSelector() labels.Selector { + return labels.SelectorFromSet(r.stableSelector().MatchLabels) +} + // ServiceAccount returns the expected proxy serviceAccount. func (r *ResourceRender) ServiceAccount() (*corev1.ServiceAccount, error) { // Set the labels based on the owning gateway name. diff --git a/internal/infrastructure/kubernetes/ratelimit/resource_provider.go b/internal/infrastructure/kubernetes/ratelimit/resource_provider.go index 50c5c8bf7f2..3336d41eec2 100644 --- a/internal/infrastructure/kubernetes/ratelimit/resource_provider.go +++ b/internal/infrastructure/kubernetes/ratelimit/resource_provider.go @@ -15,6 +15,7 @@ import ( corev1 "k8s.io/api/core/v1" policyv1 "k8s.io/api/policy/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" @@ -60,6 +61,10 @@ func (r *ResourceRender) Name() string { return InfraName } +func (r *ResourceRender) LabelSelector() labels.Selector { + return labels.SelectorFromSet(rateLimitLabels()) +} + func enablePrometheus(rl *egv1a1.RateLimit) bool { if rl != nil && rl.Telemetry != nil && diff --git a/internal/infrastructure/kubernetes/ratelimit_infra.go b/internal/infrastructure/kubernetes/ratelimit_infra.go index 514f86a1d9d..d4f5707538c 100644 --- a/internal/infrastructure/kubernetes/ratelimit_infra.go +++ b/internal/infrastructure/kubernetes/ratelimit_infra.go @@ -52,10 +52,6 @@ func (i *Infra) CreateOrUpdateRateLimitInfra(ctx context.Context) error { // DeleteRateLimitInfra removes the managed kube infra, if it doesn't exist. func (i *Infra) DeleteRateLimitInfra(ctx context.Context) error { - if err := ratelimit.Validate(ctx, i.Client.Client, i.EnvoyGateway, i.Namespace); err != nil { - return err - } - // Delete ratelimit infra do not require the uid of owner reference. r := ratelimit.NewResourceRender(i.Namespace, i.EnvoyGateway, nil) return i.delete(ctx, r) diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 60e92c2e019..ddc32a2021e 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -2431,7 +2431,7 @@ _Appears in:_ -KubernetesDaemonsetSpec defines the desired state of the Kubernetes daemonset resource. +KubernetesDaemonSetSpec defines the desired state of the Kubernetes daemonset resource. _Appears in:_ - [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index 60e92c2e019..ddc32a2021e 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -2431,7 +2431,7 @@ _Appears in:_ -KubernetesDaemonsetSpec defines the desired state of the Kubernetes daemonset resource. +KubernetesDaemonSetSpec defines the desired state of the Kubernetes daemonset resource. _Appears in:_ - [EnvoyProxyKubernetesProvider](#envoyproxykubernetesprovider) From 70c7c34d34a3746f3fc6ab224361f6515480bf4a Mon Sep 17 00:00:00 2001 From: zirain Date: Thu, 17 Oct 2024 01:50:21 +0800 Subject: [PATCH 24/56] chore: set klog (#4455) * chore: set klog Signed-off-by: zirain * lint Signed-off-by: zirain --------- Signed-off-by: zirain --- go.mod | 2 +- internal/provider/kubernetes/kubernetes.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 1a235360cd7..482af942a9a 100644 --- a/go.mod +++ b/go.mod @@ -286,7 +286,7 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/component-base v0.31.1 // indirect - k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/klog/v2 v2.130.1 k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.17.2 // indirect diff --git a/internal/provider/kubernetes/kubernetes.go b/internal/provider/kubernetes/kubernetes.go index ffef819ee07..cca90a24a17 100644 --- a/internal/provider/kubernetes/kubernetes.go +++ b/internal/provider/kubernetes/kubernetes.go @@ -11,6 +11,7 @@ import ( "time" "k8s.io/client-go/rest" + "k8s.io/klog/v2" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/cache" @@ -47,6 +48,7 @@ func New(cfg *rest.Config, svr *ec.Server, resources *message.ProviderResources) } log.SetLogger(mgrOpts.Logger) + klog.SetLogger(mgrOpts.Logger) if !ptr.Deref(svr.EnvoyGateway.Provider.Kubernetes.LeaderElection.Disable, false) { mgrOpts.LeaderElection = true From aab669dd99f0337afaaa7c22f7826d9ebd37cfeb Mon Sep 17 00:00:00 2001 From: zirain Date: Thu, 17 Oct 2024 10:46:42 +0800 Subject: [PATCH 25/56] infra: fix DeleteAllOf rbac (#4459) * infra: fix DeleteAllOf rbac Signed-off-by: zirain * fix more Signed-off-by: zirain --------- Signed-off-by: zirain --- charts/gateway-helm/templates/infra-manager-rbac.yaml | 4 ++++ internal/infrastructure/kubernetes/infra_resource.go | 7 +++++++ test/helm/gateway-helm/certjen-custom-scheduling.out.yaml | 4 ++++ test/helm/gateway-helm/control-plane-with-pdb.out.yaml | 4 ++++ test/helm/gateway-helm/default-config.out.yaml | 4 ++++ test/helm/gateway-helm/deployment-custom-topology.out.yaml | 4 ++++ test/helm/gateway-helm/deployment-images-config.out.yaml | 4 ++++ test/helm/gateway-helm/deployment-priorityclass.out.yaml | 4 ++++ test/helm/gateway-helm/envoy-gateway-config.out.yaml | 4 ++++ test/helm/gateway-helm/global-images-config.out.yaml | 4 ++++ test/helm/gateway-helm/service-annotations.out.yaml | 4 ++++ 11 files changed, 47 insertions(+) diff --git a/charts/gateway-helm/templates/infra-manager-rbac.yaml b/charts/gateway-helm/templates/infra-manager-rbac.yaml index 02382bc271c..74c0ec6282e 100644 --- a/charts/gateway-helm/templates/infra-manager-rbac.yaml +++ b/charts/gateway-helm/templates/infra-manager-rbac.yaml @@ -11,10 +11,12 @@ rules: resources: - serviceaccounts - services + - configmaps verbs: - create - get - delete + - deletecollection - patch - apiGroups: - apps @@ -25,6 +27,7 @@ rules: - create - get - delete + - deletecollection - patch - apiGroups: - autoscaling @@ -36,6 +39,7 @@ rules: - create - get - delete + - deletecollection - patch --- apiVersion: rbac.authorization.k8s.io/v1 diff --git a/internal/infrastructure/kubernetes/infra_resource.go b/internal/infrastructure/kubernetes/infra_resource.go index 04d0a2e0c1f..16cd72b9545 100644 --- a/internal/infrastructure/kubernetes/infra_resource.go +++ b/internal/infrastructure/kubernetes/infra_resource.go @@ -398,6 +398,7 @@ func (i *Infra) deleteServiceAccount(ctx context.Context, r ResourceRender) (err return i.Client.DeleteAllOf(ctx, sa, &client.DeleteAllOfOptions{ ListOptions: client.ListOptions{ + Namespace: ns, LabelSelector: r.LabelSelector(), }, }) @@ -437,6 +438,7 @@ func (i *Infra) deleteDeployment(ctx context.Context, r ResourceRender) (err err return i.Client.DeleteAllOf(ctx, deployment, &client.DeleteAllOfOptions{ ListOptions: client.ListOptions{ + Namespace: ns, LabelSelector: r.LabelSelector(), }, }) @@ -476,6 +478,7 @@ func (i *Infra) deleteDaemonSet(ctx context.Context, r ResourceRender) (err erro return i.Client.DeleteAllOf(ctx, daemonSet, &client.DeleteAllOfOptions{ ListOptions: client.ListOptions{ + Namespace: ns, LabelSelector: r.LabelSelector(), }, }) @@ -510,6 +513,7 @@ func (i *Infra) deleteConfigMap(ctx context.Context, r ResourceRender) (err erro return i.Client.DeleteAllOf(ctx, cm, &client.DeleteAllOfOptions{ ListOptions: client.ListOptions{ + Namespace: ns, LabelSelector: r.LabelSelector(), }, }) @@ -544,6 +548,7 @@ func (i *Infra) deleteService(ctx context.Context, r ResourceRender) (err error) return i.Client.DeleteAllOf(ctx, svc, &client.DeleteAllOfOptions{ ListOptions: client.ListOptions{ + Namespace: ns, LabelSelector: r.LabelSelector(), }, }) @@ -583,6 +588,7 @@ func (i *Infra) deleteHPA(ctx context.Context, r ResourceRender) (err error) { return i.Client.DeleteAllOf(ctx, hpa, &client.DeleteAllOfOptions{ ListOptions: client.ListOptions{ + Namespace: ns, LabelSelector: r.LabelSelector(), }, }) @@ -622,6 +628,7 @@ func (i *Infra) deletePDB(ctx context.Context, r ResourceRender) (err error) { return i.Client.DeleteAllOf(ctx, pdb, &client.DeleteAllOfOptions{ ListOptions: client.ListOptions{ + Namespace: ns, LabelSelector: r.LabelSelector(), }, }) diff --git a/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml b/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml index 8a1513469de..3746f12a18d 100644 --- a/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml +++ b/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml @@ -204,10 +204,12 @@ rules: resources: - serviceaccounts - services + - configmaps verbs: - create - get - delete + - deletecollection - patch - apiGroups: - apps @@ -218,6 +220,7 @@ rules: - create - get - delete + - deletecollection - patch - apiGroups: - autoscaling @@ -229,6 +232,7 @@ rules: - create - get - delete + - deletecollection - patch --- # Source: gateway-helm/templates/leader-election-rbac.yaml diff --git a/test/helm/gateway-helm/control-plane-with-pdb.out.yaml b/test/helm/gateway-helm/control-plane-with-pdb.out.yaml index a71e46fe7bd..582fab0e8b9 100644 --- a/test/helm/gateway-helm/control-plane-with-pdb.out.yaml +++ b/test/helm/gateway-helm/control-plane-with-pdb.out.yaml @@ -219,10 +219,12 @@ rules: resources: - serviceaccounts - services + - configmaps verbs: - create - get - delete + - deletecollection - patch - apiGroups: - apps @@ -233,6 +235,7 @@ rules: - create - get - delete + - deletecollection - patch - apiGroups: - autoscaling @@ -244,6 +247,7 @@ rules: - create - get - delete + - deletecollection - patch --- # Source: gateway-helm/templates/leader-election-rbac.yaml diff --git a/test/helm/gateway-helm/default-config.out.yaml b/test/helm/gateway-helm/default-config.out.yaml index 140d271c6e7..b4aac21b12c 100644 --- a/test/helm/gateway-helm/default-config.out.yaml +++ b/test/helm/gateway-helm/default-config.out.yaml @@ -204,10 +204,12 @@ rules: resources: - serviceaccounts - services + - configmaps verbs: - create - get - delete + - deletecollection - patch - apiGroups: - apps @@ -218,6 +220,7 @@ rules: - create - get - delete + - deletecollection - patch - apiGroups: - autoscaling @@ -229,6 +232,7 @@ rules: - create - get - delete + - deletecollection - patch --- # Source: gateway-helm/templates/leader-election-rbac.yaml diff --git a/test/helm/gateway-helm/deployment-custom-topology.out.yaml b/test/helm/gateway-helm/deployment-custom-topology.out.yaml index 586b64b5584..37f58022beb 100644 --- a/test/helm/gateway-helm/deployment-custom-topology.out.yaml +++ b/test/helm/gateway-helm/deployment-custom-topology.out.yaml @@ -204,10 +204,12 @@ rules: resources: - serviceaccounts - services + - configmaps verbs: - create - get - delete + - deletecollection - patch - apiGroups: - apps @@ -218,6 +220,7 @@ rules: - create - get - delete + - deletecollection - patch - apiGroups: - autoscaling @@ -229,6 +232,7 @@ rules: - create - get - delete + - deletecollection - patch --- # Source: gateway-helm/templates/leader-election-rbac.yaml diff --git a/test/helm/gateway-helm/deployment-images-config.out.yaml b/test/helm/gateway-helm/deployment-images-config.out.yaml index 10f849e1d77..8506b87b0ee 100644 --- a/test/helm/gateway-helm/deployment-images-config.out.yaml +++ b/test/helm/gateway-helm/deployment-images-config.out.yaml @@ -204,10 +204,12 @@ rules: resources: - serviceaccounts - services + - configmaps verbs: - create - get - delete + - deletecollection - patch - apiGroups: - apps @@ -218,6 +220,7 @@ rules: - create - get - delete + - deletecollection - patch - apiGroups: - autoscaling @@ -229,6 +232,7 @@ rules: - create - get - delete + - deletecollection - patch --- # Source: gateway-helm/templates/leader-election-rbac.yaml diff --git a/test/helm/gateway-helm/deployment-priorityclass.out.yaml b/test/helm/gateway-helm/deployment-priorityclass.out.yaml index 4f735c42095..3f82746416b 100644 --- a/test/helm/gateway-helm/deployment-priorityclass.out.yaml +++ b/test/helm/gateway-helm/deployment-priorityclass.out.yaml @@ -204,10 +204,12 @@ rules: resources: - serviceaccounts - services + - configmaps verbs: - create - get - delete + - deletecollection - patch - apiGroups: - apps @@ -218,6 +220,7 @@ rules: - create - get - delete + - deletecollection - patch - apiGroups: - autoscaling @@ -229,6 +232,7 @@ rules: - create - get - delete + - deletecollection - patch --- # Source: gateway-helm/templates/leader-election-rbac.yaml diff --git a/test/helm/gateway-helm/envoy-gateway-config.out.yaml b/test/helm/gateway-helm/envoy-gateway-config.out.yaml index 04159958265..9a3f5b4846b 100644 --- a/test/helm/gateway-helm/envoy-gateway-config.out.yaml +++ b/test/helm/gateway-helm/envoy-gateway-config.out.yaml @@ -206,10 +206,12 @@ rules: resources: - serviceaccounts - services + - configmaps verbs: - create - get - delete + - deletecollection - patch - apiGroups: - apps @@ -220,6 +222,7 @@ rules: - create - get - delete + - deletecollection - patch - apiGroups: - autoscaling @@ -231,6 +234,7 @@ rules: - create - get - delete + - deletecollection - patch --- # Source: gateway-helm/templates/leader-election-rbac.yaml diff --git a/test/helm/gateway-helm/global-images-config.out.yaml b/test/helm/gateway-helm/global-images-config.out.yaml index f280fc9f218..38be258c7a3 100644 --- a/test/helm/gateway-helm/global-images-config.out.yaml +++ b/test/helm/gateway-helm/global-images-config.out.yaml @@ -208,10 +208,12 @@ rules: resources: - serviceaccounts - services + - configmaps verbs: - create - get - delete + - deletecollection - patch - apiGroups: - apps @@ -222,6 +224,7 @@ rules: - create - get - delete + - deletecollection - patch - apiGroups: - autoscaling @@ -233,6 +236,7 @@ rules: - create - get - delete + - deletecollection - patch --- # Source: gateway-helm/templates/leader-election-rbac.yaml diff --git a/test/helm/gateway-helm/service-annotations.out.yaml b/test/helm/gateway-helm/service-annotations.out.yaml index ec50a16e30d..532988e919e 100644 --- a/test/helm/gateway-helm/service-annotations.out.yaml +++ b/test/helm/gateway-helm/service-annotations.out.yaml @@ -204,10 +204,12 @@ rules: resources: - serviceaccounts - services + - configmaps verbs: - create - get - delete + - deletecollection - patch - apiGroups: - apps @@ -218,6 +220,7 @@ rules: - create - get - delete + - deletecollection - patch - apiGroups: - autoscaling @@ -229,6 +232,7 @@ rules: - create - get - delete + - deletecollection - patch --- # Source: gateway-helm/templates/leader-election-rbac.yaml From 29d7020b5628545b307335e40fc4464ac94df880 Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Thu, 17 Oct 2024 17:19:09 -0700 Subject: [PATCH 26/56] feat: implement fallback for the Backend API (#4461) * feat: implement fallback for the Backend API Relates to https://github.com/envoyproxy/gateway/issues/3055 Signed-off-by: Arko Dasgupta * fix lint Signed-off-by: Arko Dasgupta --------- Signed-off-by: Arko Dasgupta --- api/v1alpha1/backend_types.go | 1 - internal/gatewayapi/route.go | 12 +- .../testdata/backend-with-fallback.in.yaml | 58 ++++++ .../testdata/backend-with-fallback.out.yaml | 180 ++++++++++++++++++ site/content/en/latest/api/extension_types.md | 1 + site/content/zh/latest/api/extension_types.md | 1 + 6 files changed, 251 insertions(+), 2 deletions(-) create mode 100644 internal/gatewayapi/testdata/backend-with-fallback.in.yaml create mode 100644 internal/gatewayapi/testdata/backend-with-fallback.out.yaml diff --git a/api/v1alpha1/backend_types.go b/api/v1alpha1/backend_types.go index a2022c2c5ef..bb407981af8 100644 --- a/api/v1alpha1/backend_types.go +++ b/api/v1alpha1/backend_types.go @@ -131,7 +131,6 @@ type BackendSpec struct { // the health of the active backends falls below 72%. // // +optional - // +notImplementedHide Fallback *bool `json:"fallback,omitempty"` } diff --git a/internal/gatewayapi/route.go b/internal/gatewayapi/route.go index 1239caadc91..f82158715a5 100644 --- a/internal/gatewayapi/route.go +++ b/internal/gatewayapi/route.go @@ -1291,6 +1291,7 @@ func (t *Translator) processDestination(backendRefContext BackendRefContext, resources, envoyProxy, ) + ds.Filters = t.processDestinationFilters(routeType, backendRefContext, parentRef, route, resources) } @@ -1672,9 +1673,18 @@ func (t *Translator) processBackendDestinationSetting(backendRef gwapiv1.Backend } } - return &ir.DestinationSetting{ + ds := &ir.DestinationSetting{ Protocol: dstProtocol, Endpoints: dstEndpoints, AddressType: dstAddrType, } + + if backend.Spec.Fallback != nil { + // set only the secondary priority, the backend defaults to a primary priority if unset. + if ptr.Deref(backend.Spec.Fallback, false) { + ds.Priority = ptr.To(uint32(1)) + } + } + + return ds } diff --git a/internal/gatewayapi/testdata/backend-with-fallback.in.yaml b/internal/gatewayapi/testdata/backend-with-fallback.in.yaml new file mode 100644 index 00000000000..667a75a6557 --- /dev/null +++ b/internal/gatewayapi/testdata/backend-with-fallback.in.yaml @@ -0,0 +1,58 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + rules: + - matches: + - path: + value: "/" + backendRefs: + - group: gateway.envoyproxy.io + kind: Backend + name: backend-1 + - group: gateway.envoyproxy.io + kind: Backend + name: backend-2 +backends: + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: Backend + metadata: + name: backend-1 + namespace: default + spec: + endpoints: + - ip: + address: 1.1.1.1 + port: 3001 + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: Backend + metadata: + name: backend-2 + namespace: default + spec: + fallback: true + endpoints: + - ip: + address: 2.2.2.2 + port: 3001 diff --git a/internal/gatewayapi/testdata/backend-with-fallback.out.yaml b/internal/gatewayapi/testdata/backend-with-fallback.out.yaml new file mode 100644 index 00000000000..74bd61795fe --- /dev/null +++ b/internal/gatewayapi/testdata/backend-with-fallback.out.yaml @@ -0,0 +1,180 @@ +backends: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: Backend + metadata: + creationTimestamp: null + name: backend-1 + namespace: default + spec: + endpoints: + - ip: + address: 1.1.1.1 + port: 3001 + status: + conditions: + - lastTransitionTime: null + message: The Backend was accepted + reason: Accepted + status: "True" + type: Accepted +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: Backend + metadata: + creationTimestamp: null + name: backend-2 + namespace: default + spec: + endpoints: + - ip: + address: 2.2.2.2 + port: 3001 + fallback: true + status: + conditions: + - lastTransitionTime: null + message: The Backend was accepted + reason: Accepted + status: "True" + type: Accepted +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + rules: + - backendRefs: + - group: gateway.envoyproxy.io + kind: Backend + name: backend-1 + - group: gateway.envoyproxy.io + kind: Backend + name: backend-2 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 1.1.1.1 + port: 3001 + weight: 1 + - addressType: IP + endpoints: + - host: 2.2.2.2 + port: 3001 + priority: 1 + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index ddc32a2021e..8a54fb2d6c6 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -384,6 +384,7 @@ _Appears in:_ | --- | --- | --- | --- | | `endpoints` | _[BackendEndpoint](#backendendpoint) array_ | true | Endpoints defines the endpoints to be used when connecting to the backend. | | `appProtocols` | _[AppProtocolType](#appprotocoltype) array_ | false | AppProtocols defines the application protocols to be supported when connecting to the backend. | +| `fallback` | _boolean_ | false | Fallback indicates whether the backend is designated as a fallback.
It is highly recommended to configure active or passive health checks to ensure that failover can be detected
when the active backends become unhealthy and to automatically readjust once the primary backends are healthy again.
The overprovisioning factor is set to 1.4, meaning the fallback backends will only start receiving traffic when
the health of the active backends falls below 72%. | #### BackendStatus diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index ddc32a2021e..8a54fb2d6c6 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -384,6 +384,7 @@ _Appears in:_ | --- | --- | --- | --- | | `endpoints` | _[BackendEndpoint](#backendendpoint) array_ | true | Endpoints defines the endpoints to be used when connecting to the backend. | | `appProtocols` | _[AppProtocolType](#appprotocoltype) array_ | false | AppProtocols defines the application protocols to be supported when connecting to the backend. | +| `fallback` | _boolean_ | false | Fallback indicates whether the backend is designated as a fallback.
It is highly recommended to configure active or passive health checks to ensure that failover can be detected
when the active backends become unhealthy and to automatically readjust once the primary backends are healthy again.
The overprovisioning factor is set to 1.4, meaning the fallback backends will only start receiving traffic when
the health of the active backends falls below 72%. | #### BackendStatus From d371d0b4ae1a74917f0a59574b14656be981a0b9 Mon Sep 17 00:00:00 2001 From: Huabing Zhao Date: Fri, 18 Oct 2024 08:24:25 +0800 Subject: [PATCH 27/56] chore: bump go control plane to 0.13.1 (#4465) * bump go control plane to 0.13.1 Signed-off-by: Huabing Zhao * bump go control plane to 0.13.1 Signed-off-by: Huabing Zhao --------- Signed-off-by: Huabing Zhao --- examples/extension-server/go.mod | 2 +- examples/extension-server/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/extension-server/go.mod b/examples/extension-server/go.mod index c4a08fecafc..92af0438105 100644 --- a/examples/extension-server/go.mod +++ b/examples/extension-server/go.mod @@ -4,7 +4,7 @@ go 1.23.1 require ( github.com/envoyproxy/gateway v1.0.2 - github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568 + github.com/envoyproxy/go-control-plane v0.13.1 github.com/urfave/cli/v2 v2.27.4 google.golang.org/grpc v1.67.1 google.golang.org/protobuf v1.35.1 diff --git a/examples/extension-server/go.sum b/examples/extension-server/go.sum index 7d995a60424..1df719e00af 100644 --- a/examples/extension-server/go.sum +++ b/examples/extension-server/go.sum @@ -11,8 +11,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568 h1:bUMUmkPtm/z62/8WiVbxtqTK8I7AzXGYn+qB8JAzAXw= -github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= +github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE= +github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= diff --git a/go.mod b/go.mod index 482af942a9a..ee7928f0a0a 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/docker/cli v27.3.1+incompatible github.com/dominikbraun/graph v0.23.0 - github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568 + github.com/envoyproxy/go-control-plane v0.13.1 github.com/envoyproxy/ratelimit v1.4.1-0.20230427142404-e2a87f41d3a7 github.com/evanphx/json-patch/v5 v5.9.0 github.com/fatih/color v1.17.0 diff --git a/go.sum b/go.sum index c5d453f9e0d..93ade68d710 100644 --- a/go.sum +++ b/go.sum @@ -222,8 +222,8 @@ github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRr github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568 h1:bUMUmkPtm/z62/8WiVbxtqTK8I7AzXGYn+qB8JAzAXw= -github.com/envoyproxy/go-control-plane v0.13.1-0.20240917224354-20d038a70568/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= +github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE= +github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= From 7293b6c7f1f90906b55ead3b58e1a1a4222977e6 Mon Sep 17 00:00:00 2001 From: Rudrakh Panigrahi Date: Fri, 18 Oct 2024 06:11:00 +0530 Subject: [PATCH 28/56] api: fix HeaderMatch list type to allow invert matches on same header (#4464) fix HeaderMatch list type to allow invert matches on same header Signed-off-by: Rudrakh Panigrahi --- api/v1alpha1/ratelimit_types.go | 9 --------- api/v1alpha1/zz_generated.deepcopy.go | 5 ----- ...way.envoyproxy.io_backendtrafficpolicies.yaml | 16 ---------------- 3 files changed, 30 deletions(-) diff --git a/api/v1alpha1/ratelimit_types.go b/api/v1alpha1/ratelimit_types.go index deb859400f8..d7a43751077 100644 --- a/api/v1alpha1/ratelimit_types.go +++ b/api/v1alpha1/ratelimit_types.go @@ -101,8 +101,6 @@ type RateLimitSelectCondition struct { // meaning, a request MUST match all the specified headers. // At least one of headers or sourceCIDR condition must be specified. // - // +listType=map - // +listMapKey=name // +optional // +kubebuilder:validation:MaxItems=16 Headers []HeaderMatch `json:"headers,omitempty"` @@ -138,13 +136,6 @@ type SourceMatch struct { // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=256 Value string `json:"value"` - - // Invert specifies whether the value match result will be inverted. - // - // +optional - // +kubebuilder:default=false - // +notImplementedHide - Invert *bool `json:"invert,omitempty"` } // HeaderMatch defines the match attributes within the HTTP Headers of the request. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index a5e8dc183ff..d6a4f9daf2f 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -5075,11 +5075,6 @@ func (in *SourceMatch) DeepCopyInto(out *SourceMatch) { *out = new(SourceMatchType) **out = **in } - if in.Invert != nil { - in, out := &in.Invert, &out.Invert - *out = new(bool) - **out = **in - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceMatch. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index c0dce593804..521ee8bcf46 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -727,19 +727,11 @@ spec: type: object maxItems: 16 type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map sourceCIDR: description: |- SourceCIDR is the client IP Address range to match on. At least one of headers or sourceCIDR condition must be specified. properties: - invert: - default: false - description: Invert specifies whether the - value match result will be inverted. - type: boolean type: default: Exact enum: @@ -870,19 +862,11 @@ spec: type: object maxItems: 16 type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map sourceCIDR: description: |- SourceCIDR is the client IP Address range to match on. At least one of headers or sourceCIDR condition must be specified. properties: - invert: - default: false - description: Invert specifies whether the - value match result will be inverted. - type: boolean type: default: Exact enum: From 5375cf0e045c12ec3f9205db0c81f76b2e678695 Mon Sep 17 00:00:00 2001 From: Arko Dasgupta Date: Thu, 17 Oct 2024 18:39:40 -0700 Subject: [PATCH 29/56] api: direct response (#4334) * api: direct response Relates to https://github.com/envoyproxy/gateway/issues/2714 Signed-off-by: Arko Dasgupta --- api/v1alpha1/httproutefilter_types.go | 20 +++++++ api/v1alpha1/shared_types.go | 4 +- api/v1alpha1/zz_generated.deepcopy.go | 41 ++++++++++++- ....envoyproxy.io_backendtrafficpolicies.yaml | 2 - ...ateway.envoyproxy.io_httproutefilters.yaml | 60 +++++++++++++++++++ site/content/en/latest/api/extension_types.md | 20 ++++++- site/content/zh/latest/api/extension_types.md | 20 ++++++- 7 files changed, 161 insertions(+), 6 deletions(-) diff --git a/api/v1alpha1/httproutefilter_types.go b/api/v1alpha1/httproutefilter_types.go index 7f56ca07d7c..9ae8be59842 100644 --- a/api/v1alpha1/httproutefilter_types.go +++ b/api/v1alpha1/httproutefilter_types.go @@ -33,6 +33,8 @@ type HTTPRouteFilter struct { type HTTPRouteFilterSpec struct { // +optional URLRewrite *HTTPURLRewriteFilter `json:"urlRewrite,omitempty"` + // +optional + DirectResponse *HTTPDirectResponseFilter `json:"directResponse,omitempty"` } // HTTPURLRewriteFilter define rewrites of HTTP URL components such as path and host @@ -49,6 +51,24 @@ type HTTPURLRewriteFilter struct { Path *HTTPPathModifier `json:"path,omitempty"` } +// HTTPDirectResponseFilter defines the configuration to return a fixed response. +type HTTPDirectResponseFilter struct { + // Content Type of the response. This will be set in the Content-Type header. + // + // +optional + ContentType *string `json:"contentType,omitempty"` + + // Body of the Response + // + // +optional + Body *CustomResponseBody `json:"body,omitempty"` + + // Status Code of the HTTP response + // If unset, defaults to 200. + // +optional + StatusCode *int `json:"statusCode,omitempty"` +} + // HTTPPathModifierType defines the type of path redirect or rewrite. type HTTPPathModifierType string diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go index 8e98b904e35..fe795c833db 100644 --- a/api/v1alpha1/shared_types.go +++ b/api/v1alpha1/shared_types.go @@ -669,7 +669,9 @@ type CustomResponse struct { ContentType *string `json:"contentType,omitempty"` // Body of the Custom Response - Body CustomResponseBody `json:"body"` + // + // +optional + Body *CustomResponseBody `json:"body,omitempty"` } // ResponseValueType defines the types of values for the response body supported by Envoy Gateway. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index d6a4f9daf2f..696c99259fb 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1148,7 +1148,11 @@ func (in *CustomResponse) DeepCopyInto(out *CustomResponse) { *out = new(string) **out = **in } - in.Body.DeepCopyInto(&out.Body) + if in.Body != nil { + in, out := &in.Body, &out.Body + *out = new(CustomResponseBody) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResponse. @@ -2688,6 +2692,36 @@ func (in *HTTPClientTimeout) DeepCopy() *HTTPClientTimeout { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPDirectResponseFilter) DeepCopyInto(out *HTTPDirectResponseFilter) { + *out = *in + if in.ContentType != nil { + in, out := &in.ContentType, &out.ContentType + *out = new(string) + **out = **in + } + if in.Body != nil { + in, out := &in.Body, &out.Body + *out = new(CustomResponseBody) + (*in).DeepCopyInto(*out) + } + if in.StatusCode != nil { + in, out := &in.StatusCode, &out.StatusCode + *out = new(int) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPDirectResponseFilter. +func (in *HTTPDirectResponseFilter) DeepCopy() *HTTPDirectResponseFilter { + if in == nil { + return nil + } + out := new(HTTPDirectResponseFilter) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPExtAuthService) DeepCopyInto(out *HTTPExtAuthService) { *out = *in @@ -2820,6 +2854,11 @@ func (in *HTTPRouteFilterSpec) DeepCopyInto(out *HTTPRouteFilterSpec) { *out = new(HTTPURLRewriteFilter) (*in).DeepCopyInto(*out) } + if in.DirectResponse != nil { + in, out := &in.DirectResponse, &out.DirectResponse + *out = new(HTTPDirectResponseFilter) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteFilterSpec. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index 521ee8bcf46..7b2e937312d 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -1035,8 +1035,6 @@ spec: description: Content Type of the response. This will be set in the Content-Type header. type: string - required: - - body type: object required: - match diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml index 7a55ec8871f..8a75fec4211 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml @@ -49,6 +49,66 @@ spec: spec: description: Spec defines the desired state of HTTPRouteFilter. properties: + directResponse: + description: HTTPDirectResponseFilter defines the configuration to + return a fixed response. + properties: + body: + description: Body of the Response + properties: + inline: + description: Inline contains the value as an inline string. + type: string + type: + description: Type is the type of method to use to read the + body value. + enum: + - Inline + - ValueRef + type: string + valueRef: + description: |- + ValueRef contains the contents of the body + specified as a local object reference. + Only a reference to ConfigMap is supported. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example + "HTTPRoute" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + required: + - type + type: object + contentType: + description: Content Type of the response. This will be set in + the Content-Type header. + type: string + statusCode: + description: |- + Status Code of the HTTP response + If unset, defaults to 200. + type: integer + type: object urlRewrite: description: HTTPURLRewriteFilter define rewrites of HTTP URL components such as path and host diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 8a54fb2d6c6..fe361099a84 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -866,7 +866,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | | `contentType` | _string_ | false | Content Type of the response. This will be set in the Content-Type header. | -| `body` | _[CustomResponseBody](#customresponsebody)_ | true | Body of the Custom Response | +| `body` | _[CustomResponseBody](#customresponsebody)_ | false | Body of the Custom Response | #### CustomResponseBody @@ -877,6 +877,7 @@ CustomResponseBody _Appears in:_ - [CustomResponse](#customresponse) +- [HTTPDirectResponseFilter](#httpdirectresponsefilter) | Field | Type | Required | Description | | --- | --- | --- | --- | @@ -1922,6 +1923,22 @@ _Appears in:_ | `idleTimeout` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ | false | IdleTimeout for an HTTP connection. Idle time is defined as a period in which there are no active requests in the connection.
Default: 1 hour. | +#### HTTPDirectResponseFilter + + + +HTTPDirectResponseFilter defines the configuration to return a fixed response. + +_Appears in:_ +- [HTTPRouteFilterSpec](#httproutefilterspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentType` | _string_ | false | Content Type of the response. This will be set in the Content-Type header. | +| `body` | _[CustomResponseBody](#customresponsebody)_ | false | Body of the Response | +| `statusCode` | _integer_ | false | Status Code of the HTTP response
If unset, defaults to 200. | + + #### HTTPExtAuthService @@ -2045,6 +2062,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | | `urlRewrite` | _[HTTPURLRewriteFilter](#httpurlrewritefilter)_ | false | | +| `directResponse` | _[HTTPDirectResponseFilter](#httpdirectresponsefilter)_ | false | | #### HTTPStatus diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index 8a54fb2d6c6..fe361099a84 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -866,7 +866,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | | `contentType` | _string_ | false | Content Type of the response. This will be set in the Content-Type header. | -| `body` | _[CustomResponseBody](#customresponsebody)_ | true | Body of the Custom Response | +| `body` | _[CustomResponseBody](#customresponsebody)_ | false | Body of the Custom Response | #### CustomResponseBody @@ -877,6 +877,7 @@ CustomResponseBody _Appears in:_ - [CustomResponse](#customresponse) +- [HTTPDirectResponseFilter](#httpdirectresponsefilter) | Field | Type | Required | Description | | --- | --- | --- | --- | @@ -1922,6 +1923,22 @@ _Appears in:_ | `idleTimeout` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ | false | IdleTimeout for an HTTP connection. Idle time is defined as a period in which there are no active requests in the connection.
Default: 1 hour. | +#### HTTPDirectResponseFilter + + + +HTTPDirectResponseFilter defines the configuration to return a fixed response. + +_Appears in:_ +- [HTTPRouteFilterSpec](#httproutefilterspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `contentType` | _string_ | false | Content Type of the response. This will be set in the Content-Type header. | +| `body` | _[CustomResponseBody](#customresponsebody)_ | false | Body of the Response | +| `statusCode` | _integer_ | false | Status Code of the HTTP response
If unset, defaults to 200. | + + #### HTTPExtAuthService @@ -2045,6 +2062,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | | `urlRewrite` | _[HTTPURLRewriteFilter](#httpurlrewritefilter)_ | false | | +| `directResponse` | _[HTTPDirectResponseFilter](#httpdirectresponsefilter)_ | false | | #### HTTPStatus From a351c4bb457dba62e4c5d39d76d2f3a6d135a0ae Mon Sep 17 00:00:00 2001 From: Huabing Zhao Date: Fri, 18 Oct 2024 10:41:10 +0800 Subject: [PATCH 30/56] Use BackendCluster to represent OIDCProvider (#4227) * gateway api translation Signed-off-by: Huabing Zhao * gateway api translation Signed-off-by: Huabing Zhao * xds translation Signed-off-by: Huabing Zhao * add cel validation Signed-off-by: Huabing Zhao * add cel validation Signed-off-by: Huabing Zhao * CEL validation Signed-off-by: Huabing Zhao * add CEL validation Signed-off-by: Huabing Zhao * add retry trigger Signed-off-by: Huabing Zhao * refactory Signed-off-by: Huabing Zhao * fix gen Signed-off-by: Huabing Zhao * add e2e test for OIDC provider with BackendCluster Signed-off-by: Huabing Zhao * add dump Signed-off-by: Huabing Zhao * fix lint Signed-off-by: Huabing Zhao * fix test Signed-off-by: Huabing Zhao * fix test Signed-off-by: Huabing Zhao * fix test Signed-off-by: Huabing Zhao * fix test Signed-off-by: Huabing Zhao * Update internal/gatewayapi/ext_service.go Co-authored-by: Arko Dasgupta Signed-off-by: Huabing Zhao * address comment Signed-off-by: Huabing Zhao * address comment Signed-off-by: Huabing Zhao * address comment Signed-off-by: Huabing Zhao * address comment Signed-off-by: Huabing Zhao * address comment Signed-off-by: Huabing Zhao * address comment Signed-off-by: Huabing Zhao --------- Signed-off-by: Huabing Zhao --- api/v1alpha1/oidc_types.go | 3 +- ...ateway.envoyproxy.io_securitypolicies.yaml | 9 +- go.mod | 2 +- go.sum | 4 +- internal/gatewayapi/backendtrafficpolicy.go | 66 +---- internal/gatewayapi/clustersettings.go | 63 +++++ internal/gatewayapi/envoyextensionpolicy.go | 42 +-- internal/gatewayapi/ext_service.go | 53 +++- internal/gatewayapi/securitypolicy.go | 170 ++++++------ ...itypolicy-with-oidc-backendcluster.in.yaml | 101 +++++++ ...typolicy-with-oidc-backendcluster.out.yaml | 256 ++++++++++++++++++ internal/ir/xds.go | 9 + internal/ir/zz_generated.deepcopy.go | 27 +- internal/xds/translator/oidc.go | 188 +++++++++---- .../xds-ir/oidc-backend-cluster-provider.yaml | 60 ++++ .../oidc-backencluster-provider.clusters.yaml | 44 +++ ...oidc-backencluster-provider.endpoints.yaml | 12 + ...oidc-backencluster-provider.listeners.yaml | 83 ++++++ .../oidc-backencluster-provider.routes.yaml | 18 ++ .../oidc-backencluster-provider.secrets.yaml | 8 + ...idc-backend-cluster-provider.clusters.yaml | 44 +++ ...dc-backend-cluster-provider.endpoints.yaml | 12 + ...dc-backend-cluster-provider.listeners.yaml | 83 ++++++ .../oidc-backend-cluster-provider.routes.yaml | 18 ++ ...oidc-backend-cluster-provider.secrets.yaml | 8 + test/cel-validation/securitypolicy_test.go | 98 +++++++ .../oidc-securitypolicy-backendcluster.yaml | 86 ++++++ test/e2e/tests/oidc-backendcluster.go | 37 +++ test/e2e/tests/oidc.go | 192 ++++++------- 29 files changed, 1464 insertions(+), 332 deletions(-) create mode 100644 internal/gatewayapi/testdata/securitypolicy-with-oidc-backendcluster.in.yaml create mode 100644 internal/gatewayapi/testdata/securitypolicy-with-oidc-backendcluster.out.yaml create mode 100644 internal/xds/translator/testdata/in/xds-ir/oidc-backend-cluster-provider.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.clusters.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.endpoints.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.listeners.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.routes.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.secrets.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.clusters.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.endpoints.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.listeners.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.routes.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.secrets.yaml create mode 100644 test/e2e/testdata/oidc-securitypolicy-backendcluster.yaml create mode 100644 test/e2e/tests/oidc-backendcluster.go diff --git a/api/v1alpha1/oidc_types.go b/api/v1alpha1/oidc_types.go index 73e1dea8c6e..78c32287cde 100644 --- a/api/v1alpha1/oidc_types.go +++ b/api/v1alpha1/oidc_types.go @@ -107,7 +107,8 @@ type OIDC struct { // OIDCProvider defines the OIDC Provider configuration. // +kubebuilder:validation:XValidation:rule="!has(self.backendRef)",message="BackendRefs must be used, backendRef is not supported." -// +kubebuilder:validation:XValidation:rule="has(self.backendRefs)? self.backendRefs.size() > 1 : true",message="Only one backendRefs is allowed." +// +kubebuilder:validation:XValidation:rule="has(self.backendSettings)? (has(self.backendSettings.retry)?(has(self.backendSettings.retry.perRetry)? !has(self.backendSettings.retry.perRetry.timeout):true):true):true",message="Retry timeout is not supported." +// +kubebuilder:validation:XValidation:rule="has(self.backendSettings)? (has(self.backendSettings.retry)?(has(self.backendSettings.retry.retryOn)? !has(self.backendSettings.retry.retryOn.httpStatusCodes):true):true):true",message="HTTPStatusCodes is not supported." type OIDCProvider struct { // BackendRefs is used to specify the address of the OIDC Provider. // If the BackendRefs is not specified, The host and port of the OIDC Provider's token endpoint diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml index ad2c81818c2..b6a040f8c42 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml @@ -3261,9 +3261,12 @@ spec: x-kubernetes-validations: - message: BackendRefs must be used, backendRef is not supported. rule: '!has(self.backendRef)' - - message: Only one backendRefs is allowed. - rule: 'has(self.backendRefs)? self.backendRefs.size() > 1 : - true' + - message: Retry timeout is not supported. + rule: has(self.backendSettings)? (has(self.backendSettings.retry)?(has(self.backendSettings.retry.perRetry)? + !has(self.backendSettings.retry.perRetry.timeout):true):true):true + - message: HTTPStatusCodes is not supported. + rule: has(self.backendSettings)? (has(self.backendSettings.retry)?(has(self.backendSettings.retry.retryOn)? + !has(self.backendSettings.retry.retryOn.httpStatusCodes):true):true):true redirectURL: description: |- The redirect URL to be used in the OIDC diff --git a/go.mod b/go.mod index ee7928f0a0a..0b7c29eb542 100644 --- a/go.mod +++ b/go.mod @@ -159,7 +159,7 @@ require ( github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/sys/sequential v0.5.0 // indirect - github.com/moby/sys/user v0.2.0 // indirect + github.com/moby/sys/user v0.3.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect diff --git a/go.sum b/go.sum index 93ade68d710..5aa804824a4 100644 --- a/go.sum +++ b/go.sum @@ -601,8 +601,8 @@ github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9Kou github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.2.0 h1:OnpapJsRp25vkhw8TFG6OLJODNh/3rEwRWtJ3kakwRM= -github.com/moby/sys/user v0.2.0/go.mod h1:RYstrcWOJpVh+6qzUqp2bU3eaRpdiQeKGlKitaH0PM8= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index 12453ea1826..b8f289a9df0 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -16,7 +16,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/utils/ptr" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" @@ -324,7 +323,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen errs = errors.Join(errs, err) } if policy.Spec.Retry != nil { - rt = t.buildRetry(policy) + rt = buildRetry(policy.Spec.Retry) } if to, err = buildClusterSettingsTimeout(policy.Spec.ClusterSettings, nil); err != nil { err = perr.WithMessage(err, "Timeout") @@ -460,7 +459,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back errs = errors.Join(errs, err) } if policy.Spec.Retry != nil { - rt = t.buildRetry(policy) + rt = buildRetry(policy.Spec.Retry) } if ct, err = buildClusterSettingsTimeout(policy.Spec.ClusterSettings, nil); err != nil { err = perr.WithMessage(err, "Timeout") @@ -812,67 +811,6 @@ func (t *Translator) buildFaultInjection(policy *egv1a1.BackendTrafficPolicy) *i return fi } -func (t *Translator) buildRetry(policy *egv1a1.BackendTrafficPolicy) *ir.Retry { - var rt *ir.Retry - if policy.Spec.Retry != nil { - prt := policy.Spec.Retry - rt = &ir.Retry{} - - if prt.NumRetries != nil { - rt.NumRetries = ptr.To(uint32(*prt.NumRetries)) - } - - if prt.RetryOn != nil { - ro := &ir.RetryOn{} - bro := false - if prt.RetryOn.HTTPStatusCodes != nil { - ro.HTTPStatusCodes = makeIrStatusSet(prt.RetryOn.HTTPStatusCodes) - bro = true - } - - if prt.RetryOn.Triggers != nil { - ro.Triggers = makeIrTriggerSet(prt.RetryOn.Triggers) - bro = true - } - - if bro { - rt.RetryOn = ro - } - } - - if prt.PerRetry != nil { - pr := &ir.PerRetryPolicy{} - bpr := false - - if prt.PerRetry.Timeout != nil { - pr.Timeout = prt.PerRetry.Timeout - bpr = true - } - - if prt.PerRetry.BackOff != nil { - if prt.PerRetry.BackOff.MaxInterval != nil || prt.PerRetry.BackOff.BaseInterval != nil { - bop := &ir.BackOffPolicy{} - if prt.PerRetry.BackOff.MaxInterval != nil { - bop.MaxInterval = prt.PerRetry.BackOff.MaxInterval - } - - if prt.PerRetry.BackOff.BaseInterval != nil { - bop.BaseInterval = prt.PerRetry.BackOff.BaseInterval - } - pr.BackOff = bop - bpr = true - } - } - - if bpr { - rt.PerRetry = pr - } - } - } - - return rt -} - func makeIrStatusSet(in []egv1a1.HTTPStatus) []ir.HTTPStatus { statusSet := sets.NewInt() for _, r := range in { diff --git a/internal/gatewayapi/clustersettings.go b/internal/gatewayapi/clustersettings.go index 742b026249e..260c1db84d3 100644 --- a/internal/gatewayapi/clustersettings.go +++ b/internal/gatewayapi/clustersettings.go @@ -71,6 +71,8 @@ func translateTrafficFeatures(policy *egv1a1.ClusterSettings) (*ir.TrafficFeatur ret.HTTP2 = h2 } + ret.Retry = buildRetry(policy.Retry) + // If nothing was set in any of the above calls, return nil instead of an empty // container var empty ir.TrafficFeatures @@ -507,3 +509,64 @@ func translateDNS(policy egv1a1.ClusterSettings) *ir.DNS { DNSRefreshRate: policy.DNS.DNSRefreshRate, } } + +func buildRetry(r *egv1a1.Retry) *ir.Retry { + if r == nil { + return nil + } + + rt := &ir.Retry{} + + if r.NumRetries != nil { + rt.NumRetries = ptr.To(uint32(*r.NumRetries)) + } + + if r.RetryOn != nil { + ro := &ir.RetryOn{} + bro := false + if r.RetryOn.HTTPStatusCodes != nil { + ro.HTTPStatusCodes = makeIrStatusSet(r.RetryOn.HTTPStatusCodes) + bro = true + } + + if r.RetryOn.Triggers != nil { + ro.Triggers = makeIrTriggerSet(r.RetryOn.Triggers) + bro = true + } + + if bro { + rt.RetryOn = ro + } + } + + if r.PerRetry != nil { + pr := &ir.PerRetryPolicy{} + bpr := false + + if r.PerRetry.Timeout != nil { + pr.Timeout = r.PerRetry.Timeout + bpr = true + } + + if r.PerRetry.BackOff != nil { + if r.PerRetry.BackOff.MaxInterval != nil || r.PerRetry.BackOff.BaseInterval != nil { + bop := &ir.BackOffPolicy{} + if r.PerRetry.BackOff.MaxInterval != nil { + bop.MaxInterval = r.PerRetry.BackOff.MaxInterval + } + + if r.PerRetry.BackOff.BaseInterval != nil { + bop.BaseInterval = r.PerRetry.BackOff.BaseInterval + } + pr.BackOff = bop + bpr = true + } + } + + if bpr { + rt.PerRetry = pr + } + } + + return rt +} diff --git a/internal/gatewayapi/envoyextensionpolicy.go b/internal/gatewayapi/envoyextensionpolicy.go index 47394587529..5e61f2eb3aa 100644 --- a/internal/gatewayapi/envoyextensionpolicy.go +++ b/internal/gatewayapi/envoyextensionpolicy.go @@ -411,7 +411,7 @@ func (t *Translator) buildExtProcs(policy *egv1a1.EnvoyExtensionPolicy, resource for idx, ep := range policy.Spec.ExtProc { name := irConfigNameForExtProc(policy, idx) - extProcIR, err := t.buildExtProc(name, utils.NamespacedName(policy), ep, idx, resources, envoyProxy) + extProcIR, err := t.buildExtProc(name, policy, ep, idx, resources, envoyProxy) if err != nil { return nil, err } @@ -422,59 +422,33 @@ func (t *Translator) buildExtProcs(policy *egv1a1.EnvoyExtensionPolicy, resource func (t *Translator) buildExtProc( name string, - policyNamespacedName types.NamespacedName, + policy *egv1a1.EnvoyExtensionPolicy, extProc egv1a1.ExtProc, extProcIdx int, resources *resource.Resources, envoyProxy *egv1a1.EnvoyProxy, ) (*ir.ExtProc, error) { var ( - ds *ir.DestinationSetting + rd *ir.RouteDestination authority string err error ) - var dsl []*ir.DestinationSetting - for i := range extProc.BackendRefs { - if err = t.validateExtServiceBackendReference( - &extProc.BackendRefs[i].BackendObjectReference, - policyNamespacedName.Namespace, - egv1a1.KindEnvoyExtensionPolicy, - resources); err != nil { - return nil, err - } - - ds, err = t.processExtServiceDestination( - &extProc.BackendRefs[i], - policyNamespacedName, - egv1a1.KindEnvoyExtensionPolicy, - ir.GRPC, - resources, - envoyProxy, - ) - if err != nil { - return nil, err - } - - dsl = append(dsl, ds) - } - - rd := ir.RouteDestination{ - Name: irIndexedExtServiceDestinationName(policyNamespacedName, egv1a1.KindEnvoyExtensionPolicy, extProcIdx), - Settings: dsl, + if rd, err = t.translateExtServiceBackendRefs(policy, extProc.BackendRefs, ir.GRPC, resources, envoyProxy, extProcIdx); err != nil { + return nil, err } if extProc.BackendRefs[0].Port != nil { authority = fmt.Sprintf( "%s.%s:%d", extProc.BackendRefs[0].Name, - NamespaceDerefOr(extProc.BackendRefs[0].Namespace, policyNamespacedName.Namespace), + NamespaceDerefOr(extProc.BackendRefs[0].Namespace, policy.Namespace), *extProc.BackendRefs[0].Port) } else { authority = fmt.Sprintf( "%s.%s", extProc.BackendRefs[0].Name, - NamespaceDerefOr(extProc.BackendRefs[0].Namespace, policyNamespacedName.Namespace)) + NamespaceDerefOr(extProc.BackendRefs[0].Namespace, policy.Namespace)) } traffic, err := translateTrafficFeatures(extProc.BackendCluster.BackendSettings) @@ -484,7 +458,7 @@ func (t *Translator) buildExtProc( extProcIR := &ir.ExtProc{ Name: name, - Destination: rd, + Destination: *rd, Traffic: traffic, Authority: authority, } diff --git a/internal/gatewayapi/ext_service.go b/internal/gatewayapi/ext_service.go index 7e13b101011..e7ab19036ee 100644 --- a/internal/gatewayapi/ext_service.go +++ b/internal/gatewayapi/ext_service.go @@ -12,15 +12,66 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/gatewayapi/resource" "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/utils" ) -// TODO: zhaohuabing combine this function with the one in the route translator +// translateExtServiceBackendRefs translates external service backend references to route destinations. +func (t *Translator) translateExtServiceBackendRefs( + policy client.Object, + backendRefs []egv1a1.BackendRef, + protocol ir.AppProtocol, + resources *resource.Resources, + envoyProxy *egv1a1.EnvoyProxy, + index int, // index is used to differentiate between multiple external services in the same policy +) (*ir.RouteDestination, error) { + var ( + rs *ir.RouteDestination + ds []*ir.DestinationSetting + err error + ) + + if len(backendRefs) == 0 { + return nil, errors.New("no backendRefs found for external service") + } + + pnn := utils.NamespacedName(policy) + for _, backendRef := range backendRefs { + if err = t.validateExtServiceBackendReference( + &backendRef.BackendObjectReference, + policy.GetNamespace(), + policy.GetObjectKind().GroupVersionKind().Kind, + resources); err != nil { + return nil, err + } + + var extServiceDest *ir.DestinationSetting + if extServiceDest, err = t.processExtServiceDestination( + &backendRef, + pnn, + policy.GetObjectKind().GroupVersionKind().Kind, + protocol, + resources, + envoyProxy, + ); err != nil { + return nil, err + } + ds = append(ds, extServiceDest) + } + + rs = &ir.RouteDestination{ + Name: irIndexedExtServiceDestinationName(pnn, policy.GetObjectKind().GroupVersionKind().Kind, index), + Settings: ds, + } + return rs, nil +} + func (t *Translator) processExtServiceDestination( backendRef *egv1a1.BackendRef, policyNamespacedName types.NamespacedName, diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go index 2ab9670d501..c9289bbcfb3 100644 --- a/internal/gatewayapi/securitypolicy.go +++ b/internal/gatewayapi/securitypolicy.go @@ -329,7 +329,6 @@ func (t *Translator) translateSecurityPolicyForRoute( var ( cors *ir.CORS jwt *ir.JWT - oidc *ir.OIDC basicAuth *ir.BasicAuth authorization *ir.Authorization err, errs error @@ -343,15 +342,6 @@ func (t *Translator) translateSecurityPolicyForRoute( jwt = t.buildJWT(policy.Spec.JWT) } - if policy.Spec.OIDC != nil { - if oidc, err = t.buildOIDC( - policy, - resources); err != nil { - err = perr.WithMessage(err, "OIDC") - errs = errors.Join(errs, err) - } - } - if policy.Spec.BasicAuth != nil { if basicAuth, err = t.buildBasicAuth( policy, @@ -388,6 +378,18 @@ func (t *Translator) translateSecurityPolicyForRoute( errs = errors.Join(errs, err) } } + + var oidc *ir.OIDC + if policy.Spec.OIDC != nil { + if oidc, err = t.buildOIDC( + policy, + resources, + gtwCtx.envoyProxy); err != nil { + err = perr.WithMessage(err, "OIDC") + errs = errors.Join(errs, err) + } + } + irKey := t.getIRKey(gtwCtx.Gateway) for _, listener := range parentRefCtx.listeners { irListener := xdsIR[irKey].GetHTTPListener(irListenerName(listener)) @@ -445,7 +447,8 @@ func (t *Translator) translateSecurityPolicyForGateway( if policy.Spec.OIDC != nil { if oidc, err = t.buildOIDC( policy, - resources); err != nil { + resources, + gateway.envoyProxy); err != nil { err = perr.WithMessage(err, "OIDC") errs = errors.Join(errs, err) } @@ -566,19 +569,30 @@ func (t *Translator) buildJWT(jwt *egv1a1.JWT) *ir.JWT { func (t *Translator) buildOIDC( policy *egv1a1.SecurityPolicy, resources *resource.Resources, + envoyProxy *egv1a1.EnvoyProxy, ) (*ir.OIDC, error) { var ( - oidc = policy.Spec.OIDC - clientSecret *corev1.Secret - provider *ir.OIDCProvider - err error + oidc = policy.Spec.OIDC + provider *ir.OIDCProvider + clientSecret *corev1.Secret + redirectURL = defaultRedirectURL + redirectPath = defaultRedirectPath + logoutPath = defaultLogoutPath + forwardAccessToken = defaultForwardAccessToken + refreshToken = defaultRefreshToken + err error ) + if provider, err = t.buildOIDCProvider(policy, resources, envoyProxy); err != nil { + return nil, err + } + from := crossNamespaceFrom{ group: egv1a1.GroupName, kind: resource.KindSecurityPolicy, namespace: policy.Namespace, } + if clientSecret, err = t.validateSecretRef( false, from, oidc.ClientSecret, resources); err != nil { return nil, err @@ -591,25 +605,8 @@ func (t *Translator) buildOIDC( clientSecret.Namespace, clientSecret.Name) } - // Discover the token and authorization endpoints from the issuer's - // well-known url if not explicitly specified - if provider, err = discoverEndpointsFromIssuer(&oidc.Provider); err != nil { - return nil, err - } - - if err = validateTokenEndpoint(provider.TokenEndpoint); err != nil { - return nil, err - } scopes := appendOpenidScopeIfNotExist(oidc.Scopes) - var ( - redirectURL = defaultRedirectURL - redirectPath = defaultRedirectPath - logoutPath = defaultLogoutPath - forwardAccessToken = defaultForwardAccessToken - refreshToken = defaultRefreshToken - ) - if oidc.RedirectURL != nil { path, err := extractRedirectPath(*oidc.RedirectURL) if err != nil { @@ -668,6 +665,62 @@ func (t *Translator) buildOIDC( }, nil } +func (t *Translator) buildOIDCProvider(policy *egv1a1.SecurityPolicy, resources *resource.Resources, envoyProxy *egv1a1.EnvoyProxy) (*ir.OIDCProvider, error) { + var ( + provider = policy.Spec.OIDC.Provider + tokenEndpoint string + authorizationEndpoint string + protocol ir.AppProtocol + rd *ir.RouteDestination + traffic *ir.TrafficFeatures + err error + ) + + // Discover the token and authorization endpoints from the issuer's + // well-known url if not explicitly specified + if provider.TokenEndpoint == nil || provider.AuthorizationEndpoint == nil { + tokenEndpoint, authorizationEndpoint, err = fetchEndpointsFromIssuer(provider.Issuer) + if err != nil { + return nil, fmt.Errorf("error fetching endpoints from issuer: %w", err) + } + } else { + tokenEndpoint = *provider.TokenEndpoint + authorizationEndpoint = *provider.AuthorizationEndpoint + } + + if err = validateTokenEndpoint(tokenEndpoint); err != nil { + return nil, err + } + + u, err := url.Parse(tokenEndpoint) + if err != nil { + return nil, err + } + + if u.Scheme == "https" { + protocol = ir.HTTPS + } else { + protocol = ir.HTTP + } + + if len(provider.BackendRefs) > 0 { + if rd, err = t.translateExtServiceBackendRefs(policy, provider.BackendRefs, protocol, resources, envoyProxy, 0); err != nil { + return nil, err + } + } + + if traffic, err = translateTrafficFeatures(provider.BackendSettings); err != nil { + return nil, err + } + + return &ir.OIDCProvider{ + Destination: rd, + Traffic: traffic, + AuthorizationEndpoint: authorizationEndpoint, + TokenEndpoint: tokenEndpoint, + }, nil +} + func extractRedirectPath(redirectURL string) (string, error) { schemeDelimiter := strings.Index(redirectURL, "://") if schemeDelimiter <= 0 { @@ -712,26 +765,6 @@ type OpenIDConfig struct { AuthorizationEndpoint string `json:"authorization_endpoint"` } -// discoverEndpointsFromIssuer discovers the token and authorization endpoints from the issuer's well-known url -// return error if failed to fetch the well-known configuration -func discoverEndpointsFromIssuer(provider *egv1a1.OIDCProvider) (*ir.OIDCProvider, error) { - if provider.TokenEndpoint == nil || provider.AuthorizationEndpoint == nil { - tokenEndpoint, authorizationEndpoint, err := fetchEndpointsFromIssuer(provider.Issuer) - if err != nil { - return nil, fmt.Errorf("error fetching endpoints from issuer: %w", err) - } - return &ir.OIDCProvider{ - TokenEndpoint: tokenEndpoint, - AuthorizationEndpoint: authorizationEndpoint, - }, nil - } - - return &ir.OIDCProvider{ - TokenEndpoint: *provider.TokenEndpoint, - AuthorizationEndpoint: *provider.AuthorizationEndpoint, - }, nil -} - func fetchEndpointsFromIssuer(issuerURL string) (string, string, error) { // Fetch the OpenID configuration from the issuer URL resp, err := http.Get(fmt.Sprintf("%s/.well-known/openid-configuration", issuerURL)) @@ -811,7 +844,7 @@ func (t *Translator) buildExtAuth(policy *egv1a1.SecurityPolicy, resources *reso grpc = policy.Spec.ExtAuth.GRPC backends *egv1a1.BackendCluster protocol ir.AppProtocol - ds []*ir.DestinationSetting + rd *ir.RouteDestination authority string err error traffic *ir.TrafficFeatures @@ -833,12 +866,12 @@ func (t *Translator) buildExtAuth(policy *egv1a1.SecurityPolicy, resources *reso backends = &grpc.BackendCluster protocol = ir.GRPC } - pnn := utils.NamespacedName(policy) - for _, backendRef := range backends.BackendRefs { - if err = t.validateExtServiceBackendReference(&backendRef.BackendObjectReference, policy.Namespace, policy.Kind, resources); err != nil { - return nil, err - } + if rd, err = t.translateExtServiceBackendRefs(policy, backends.BackendRefs, protocol, resources, envoyProxy, 0); err != nil { + return nil, err + } + + for _, backendRef := range backends.BackendRefs { // Authority is the calculated hostname that will be used as the Authority header. // If there are multiple backend referenced, simply use the first one - there are no good answers here. // When translated to XDS, the authority is used on the filter level not on the cluster level. @@ -846,23 +879,6 @@ func (t *Translator) buildExtAuth(policy *egv1a1.SecurityPolicy, resources *reso if authority == "" { authority = backendRefAuthority(resources, &backendRef.BackendObjectReference, policy) } - - extServiceDest, err := t.processExtServiceDestination( - &backendRef, - pnn, - resource.KindSecurityPolicy, - protocol, - resources, - envoyProxy, - ) - if err != nil { - return nil, err - } - ds = append(ds, extServiceDest) - } - rd := ir.RouteDestination{ - Name: irIndexedExtServiceDestinationName(pnn, resource.KindSecurityPolicy, 0), - Settings: ds, } if traffic, err = translateTrafficFeatures(backends.BackendSettings); err != nil { @@ -878,14 +894,14 @@ func (t *Translator) buildExtAuth(policy *egv1a1.SecurityPolicy, resources *reso if http != nil { extAuth.HTTP = &ir.HTTPExtAuthService{ - Destination: rd, + Destination: *rd, Authority: authority, Path: ptr.Deref(http.Path, ""), HeadersToBackend: http.HeadersToBackend, } } else { extAuth.GRPC = &ir.GRPCExtAuthService{ - Destination: rd, + Destination: *rd, Authority: authority, } } diff --git a/internal/gatewayapi/testdata/securitypolicy-with-oidc-backendcluster.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-oidc-backendcluster.in.yaml new file mode 100644 index 00000000000..67b051e4b31 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-oidc-backendcluster.in.yaml @@ -0,0 +1,101 @@ +secrets: +- apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway + name: client1-secret + data: + client-secret: Y2xpZW50MTpzZWNyZXQK +- apiVersion: v1 + kind: Secret + metadata: + namespace: envoy-gateway-system + name: envoy-oidc-hmac + data: + hmac-secret: qrOYACHXoe7UEDI/raOjNSx+Z9ufXSc/22C3T6X/zPY= +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - www.example.com + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: http + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +backends: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: Backend + metadata: + name: backend-fqdn + namespace: envoy-gateway + spec: + endpoints: + - fqdn: + hostname: 'oauth.foo.com' + port: 443 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway + uid: b8284d0f-de82-4c65-b204-96a0d3f258a1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + oidc: + provider: + backendRefs: + - group: gateway.envoyproxy.io + kind: Backend + name: backend-fqdn + port: 443 + backendSettings: + retry: + numRetries: 3 + perRetry: + backOff: + baseInterval: 1s + maxInterval: 5s + retryOn: + triggers: ["5xx", "gateway-error", "reset"] + issuer: "https://oauth.foo.com" + authorizationEndpoint: "https://oauth.foo.com/oauth2/v2/auth" + tokenEndpoint: "https://oauth.foo.com/token" + clientID: "client1.apps.googleusercontent.com" + clientSecret: + name: "client1-secret" + redirectURL: "https://www.example.com/bar/oauth2/callback" + logoutPath: "/bar/logout" + forwardAccessToken: true + defaultTokenTTL: 30m + refreshToken: true + defaultRefreshTokenTTL: 24h diff --git a/internal/gatewayapi/testdata/securitypolicy-with-oidc-backendcluster.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-oidc-backendcluster.out.yaml new file mode 100644 index 00000000000..d6f0c4dbc47 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-with-oidc-backendcluster.out.yaml @@ -0,0 +1,256 @@ +backends: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: Backend + metadata: + creationTimestamp: null + name: backend-fqdn + namespace: envoy-gateway + spec: + endpoints: + - fqdn: + hostname: oauth.foo.com + port: 443 + status: + conditions: + - lastTransitionTime: null + message: The Backend was accepted + reason: Accepted + status: "True" + type: Accepted +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - www.example.com + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: envoy-gateway + sectionName: http +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + name: envoy-gateway/gateway-1 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: envoy-gateway + uid: b8284d0f-de82-4c65-b204-96a0d3f258a1 + spec: + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: + group: null + kind: null + name: client1-secret + defaultRefreshTokenTTL: 24h0m0s + defaultTokenTTL: 30m0s + forwardAccessToken: true + logoutPath: /bar/logout + provider: + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + backendRefs: + - group: gateway.envoyproxy.io + kind: Backend + name: backend-fqdn + port: 443 + backendSettings: + retry: + numRetries: 3 + perRetry: + backOff: + baseInterval: 1s + maxInterval: 5s + retryOn: + triggers: + - 5xx + - gateway-error + - reset + issuer: https://oauth.foo.com + tokenEndpoint: https://oauth.foo.com/token + redirectURL: https://www.example.com/bar/oauth2/callback + refreshToken: true + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +xdsIR: + envoy-gateway/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: http + name: envoy-gateway/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: www.example.com + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/www_example_com + pathMatch: + distinct: false + name: "" + prefix: /foo + security: + oidc: + clientID: client1.apps.googleusercontent.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + cookieSuffix: b0a1b740 + defaultRefreshTokenTTL: 24h0m0s + defaultTokenTTL: 30m0s + forwardAccessToken: true + hmacSecret: qrOYACHXoe7UEDI/raOjNSx+Z9ufXSc/22C3T6X/zPY= + logoutPath: /bar/logout + name: securitypolicy/envoy-gateway/policy-for-gateway + provider: + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + destination: + name: securitypolicy/envoy-gateway/policy-for-gateway/0 + settings: + - addressType: FQDN + endpoints: + - host: oauth.foo.com + port: 443 + protocol: HTTPS + weight: 1 + tokenEndpoint: https://oauth.foo.com/token + traffic: + retry: + numRetries: 3 + perRetry: + backOff: + baseInterval: 1s + maxInterval: 5s + retryOn: + triggers: + - 5xx + - gateway-error + - reset + redirectPath: /bar/oauth2/callback + redirectURL: https://www.example.com/bar/oauth2/callback + refreshToken: true + scopes: + - openid diff --git a/internal/ir/xds.go b/internal/ir/xds.go index 49d6fdbf064..fdcace324f5 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -848,7 +848,16 @@ type OIDC struct { CookieDomain *string `json:"cookieDomain,omitempty"` } +// OIDCProvider defines the schema for the OIDC Provider. +// +// +k8s:deepcopy-gen=true type OIDCProvider struct { + // Destination defines the destination for the OIDC Provider. + Destination *RouteDestination `json:"destination,omitempty"` + + // Traffic contains configuration for traffic features for the OIDC Provider + Traffic *TrafficFeatures `json:"traffic,omitempty"` + // The OIDC Provider's [authorization endpoint](https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationEndpoint). AuthorizationEndpoint string `json:"authorizationEndpoint,omitempty"` diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 1a0185bbb9f..3c0c1135f44 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -1900,7 +1900,7 @@ func (in *Metrics) DeepCopy() *Metrics { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OIDC) DeepCopyInto(out *OIDC) { *out = *in - out.Provider = in.Provider + in.Provider.DeepCopyInto(&out.Provider) if in.ClientSecret != nil { in, out := &in.ClientSecret, &out.ClientSecret *out = make([]byte, len(*in)) @@ -1953,6 +1953,31 @@ func (in *OIDC) DeepCopy() *OIDC { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDCProvider) DeepCopyInto(out *OIDCProvider) { + *out = *in + if in.Destination != nil { + in, out := &in.Destination, &out.Destination + *out = new(RouteDestination) + (*in).DeepCopyInto(*out) + } + if in.Traffic != nil { + in, out := &in.Traffic, &out.Traffic + *out = new(TrafficFeatures) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCProvider. +func (in *OIDCProvider) DeepCopy() *OIDCProvider { + if in == nil { + return nil + } + out := new(OIDCProvider) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OpenTelemetryAccessLog) DeepCopyInto(out *OpenTelemetryAccessLog) { *out = *in diff --git a/internal/xds/translator/oidc.go b/internal/xds/translator/oidc.go index 41228a1d209..963b7c8046d 100644 --- a/internal/xds/translator/oidc.go +++ b/internal/xds/translator/oidc.go @@ -102,14 +102,24 @@ func oauth2FilterName(oidc *ir.OIDC) string { } func oauth2Config(oidc *ir.OIDC) (*oauth2v3.OAuth2, error) { - cluster, err := url2Cluster(oidc.Provider.TokenEndpoint) - if err != nil { - return nil, err - } - if cluster.endpointType == EndpointTypeStatic { - return nil, fmt.Errorf( - "static IP cluster is not allowed: %s", - oidc.Provider.TokenEndpoint) + var ( + tokenEndpointCluster string + err error + ) + + if oidc.Provider.Destination != nil && len(oidc.Provider.Destination.Settings) > 0 { + tokenEndpointCluster = oidc.Provider.Destination.Name + } else { + var cluster *urlCluster + if cluster, err = url2Cluster(oidc.Provider.TokenEndpoint); err != nil { + return nil, err + } + if cluster.endpointType == EndpointTypeStatic { + return nil, fmt.Errorf( + "static IP cluster is not allowed: %s", + oidc.Provider.TokenEndpoint) + } + tokenEndpointCluster = cluster.name } // Envoy OAuth2 filter deletes the HTTP authorization header by default, which surprises users. @@ -126,7 +136,7 @@ func oauth2Config(oidc *ir.OIDC) (*oauth2v3.OAuth2, error) { TokenEndpoint: &corev3.HttpUri{ Uri: oidc.Provider.TokenEndpoint, HttpUpstreamType: &corev3.HttpUri_Cluster{ - Cluster: cluster.name, + Cluster: tokenEndpointCluster, }, Timeout: &durationpb.Duration{ Seconds: defaultExtServiceRequestTimeout, @@ -210,9 +220,55 @@ func oauth2Config(oidc *ir.OIDC) (*oauth2v3.OAuth2, error) { oauth2.Config.Credentials.CookieDomain = *oidc.CookieDomain } + // Set the retry policy if it exists. + if oidc.Provider.Traffic != nil && oidc.Provider.Traffic.Retry != nil { + var rp *corev3.RetryPolicy + if rp, err = buildNonRouteRetryPolicy(oidc.Provider.Traffic.Retry); err != nil { + return nil, err + } + oauth2.Config.RetryPolicy = rp + } return oauth2, nil } +func buildNonRouteRetryPolicy(rr *ir.Retry) (*corev3.RetryPolicy, error) { + rp := &corev3.RetryPolicy{ + RetryOn: retryDefaultRetryOn, + } + + // These two fields in the RetryPolicy are just for route-level retries, they are not used for non-route retries. + // retry.PerRetry.Timeout + // retry.RetryOn.HTTPStatusCodes + + if rr.PerRetry != nil && rr.PerRetry.BackOff != nil { + rp.RetryBackOff = &corev3.BackoffStrategy{ + BaseInterval: &durationpb.Duration{ + Seconds: int64(rr.PerRetry.BackOff.BaseInterval.Seconds()), + }, + MaxInterval: &durationpb.Duration{ + Seconds: int64(rr.PerRetry.BackOff.MaxInterval.Seconds()), + }, + } + } + + if rr.NumRetries != nil { + rp.NumRetries = &wrappers.UInt32Value{ + Value: *rr.NumRetries, + } + } + + if rr.RetryOn != nil { + if len(rr.RetryOn.Triggers) > 0 { + if ro, err := buildRetryOn(rr.RetryOn.Triggers); err == nil { + rp.RetryOn = ro + } else { + return nil, err + } + } + } + return rp, nil +} + // routeContainsOIDC returns true if OIDC exists for the provided route. func routeContainsOIDC(irRoute *ir.HTTPRoute) bool { if irRoute != nil && @@ -226,7 +282,7 @@ func routeContainsOIDC(irRoute *ir.HTTPRoute) bool { func (*oidc) patchResources(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute, ) error { - if err := createOAuth2TokenEndpointClusters(tCtx, routes); err != nil { + if err := createOAuthServerClusters(tCtx, routes); err != nil { return err } if err := createOAuth2Secrets(tCtx, routes); err != nil { @@ -235,9 +291,8 @@ func (*oidc) patchResources(tCtx *types.ResourceVersionTable, return nil } -// createOAuth2TokenEndpointClusters creates token endpoint clusters from the -// provided routes, if needed. -func createOAuth2TokenEndpointClusters(tCtx *types.ResourceVersionTable, +// createOAuthServerClusters creates clusters for the OAuth2 server. +func createOAuthServerClusters(tCtx *types.ResourceVersionTable, routes []*ir.HTTPRoute, ) error { if tCtx == nil || tCtx.XdsResources == nil { @@ -250,59 +305,78 @@ func createOAuth2TokenEndpointClusters(tCtx *types.ResourceVersionTable, continue } - var ( - cluster *urlCluster - ds *ir.DestinationSetting - tSocket *corev3.TransportSocket - err error - ) + oidc := route.Security.OIDC - cluster, err = url2Cluster(route.Security.OIDC.Provider.TokenEndpoint) - if err != nil { - errs = errors.Join(errs, err) - continue + // If the OIDC provider has a destination, use it. + if oidc.Provider.Destination != nil && len(oidc.Provider.Destination.Settings) > 0 { + if err := createExtServiceXDSCluster( + oidc.Provider.Destination, oidc.Provider.Traffic, tCtx); err != nil && !errors.Is( + err, ErrXdsClusterExists) { + errs = errors.Join(errs, err) + } + } else { + // Create a cluster with the token endpoint url. + if err := createOAuth2TokenEndpointCluster(tCtx, oidc.Provider.TokenEndpoint); err != nil { + errs = errors.Join(errs, err) + } } + } - // EG does not support static IP clusters for token endpoint clusters. - // This validation could be removed since it's already validated in the - // Gateway API translator. - if cluster.endpointType == EndpointTypeStatic { - errs = errors.Join(errs, fmt.Errorf( - "static IP cluster is not allowed: %s", - route.Security.OIDC.Provider.TokenEndpoint)) - continue - } + return errs +} - ds = &ir.DestinationSetting{ - Weight: ptr.To[uint32](1), - Endpoints: []*ir.DestinationEndpoint{ - ir.NewDestEndpoint( - cluster.hostname, - cluster.port), - }, - } +// createOAuth2TokenEndpointClusters creates token endpoint clusters from the +// provided routes, if needed. +func createOAuth2TokenEndpointCluster(tCtx *types.ResourceVersionTable, + tokenEndpoint string, +) error { + var ( + cluster *urlCluster + ds *ir.DestinationSetting + tSocket *corev3.TransportSocket + err error + ) + + if cluster, err = url2Cluster(tokenEndpoint); err != nil { + return err + } - clusterArgs := &xdsClusterArgs{ - name: cluster.name, - settings: []*ir.DestinationSetting{ds}, - tSocket: tSocket, - endpointType: cluster.endpointType, - } - if cluster.tls { - tSocket, err = buildXdsUpstreamTLSSocket(cluster.hostname) - if err != nil { - errs = errors.Join(errs, err) - continue - } - clusterArgs.tSocket = tSocket - } + // EG does not support static IP clusters for token endpoint clusters. + // This validation could be removed since it's already validated in the + // Gateway API translator. + if cluster.endpointType == EndpointTypeStatic { + return fmt.Errorf( + "static IP cluster is not allowed: %s", + tokenEndpoint) + } - if err = addXdsCluster(tCtx, clusterArgs); err != nil && !errors.Is(err, ErrXdsClusterExists) { - errs = errors.Join(errs, err) + ds = &ir.DestinationSetting{ + Weight: ptr.To[uint32](1), + Endpoints: []*ir.DestinationEndpoint{ + ir.NewDestEndpoint( + cluster.hostname, + cluster.port), + }, + } + + clusterArgs := &xdsClusterArgs{ + name: cluster.name, + settings: []*ir.DestinationSetting{ds}, + tSocket: tSocket, + endpointType: cluster.endpointType, + } + if cluster.tls { + if tSocket, err = buildXdsUpstreamTLSSocket(cluster.hostname); err != nil { + return err } + clusterArgs.tSocket = tSocket } - return errs + if err = addXdsCluster(tCtx, clusterArgs); err != nil && !errors.Is(err, ErrXdsClusterExists) { + return err + } + + return err } // createOAuth2Secrets creates OAuth2 client and HMAC secrets from the provided diff --git a/internal/xds/translator/testdata/in/xds-ir/oidc-backend-cluster-provider.yaml b/internal/xds/translator/testdata/in/xds-ir/oidc-backend-cluster-provider.yaml new file mode 100644 index 00000000000..6fc9a045bca --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/oidc-backend-cluster-provider.yaml @@ -0,0 +1,60 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + pathMatch: + exact: "baz" + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + security: + oidc: + name: securitypolicy/envoy-gateway/policy-for-gateway + clientID: client1.apps.googleusercontent.com + clientSecret: Y2xpZW50MTpzZWNyZXQK + cookieSuffix: b0a1b740 + defaultRefreshTokenTTL: 24h0m0s + defaultTokenTTL: 30m0s + forwardAccessToken: true + hmacSecret: qrOYACHXoe7UEDI/raOjNSx+Z9ufXSc/22C3T6X/zPY= + logoutPath: /bar/logout + provider: + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + destination: + name: securitypolicy/envoy-gateway/policy-for-gateway/0 + settings: + - addressType: FQDN + endpoints: + - host: oauth.foo.com + port: 443 + protocol: HTTPS + weight: 1 + tokenEndpoint: https://oauth.foo.com/token + traffic: + retry: + numRetries: 3 + perRetry: + backOff: + baseInterval: 1s + maxInterval: 5s + retryOn: + triggers: + - "5xx" + - gateway-error + - reset + redirectPath: /bar/oauth2/callback + redirectURL: https://www.example.com/bar/oauth2/callback + refreshToken: true + scopes: + - openid diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.clusters.yaml new file mode 100644 index 00000000000..863e761bf9a --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.clusters.yaml @@ -0,0 +1,44 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: securitypolicy/envoy-gateway/policy-for-gateway/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: oauth.foo.com + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: securitypolicy/envoy-gateway/policy-for-gateway/0/backend/0 + name: securitypolicy/envoy-gateway/policy-for-gateway/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + type: STRICT_DNS diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.endpoints.yaml new file mode 100644 index 00000000000..6c69841c2ad --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.listeners.yaml new file mode 100644 index 00000000000..3addb294484 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.listeners.yaml @@ -0,0 +1,83 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - disabled: true + name: envoy.filters.http.oauth2/securitypolicy/envoy-gateway/policy-for-gateway + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2 + config: + authScopes: + - openid + authType: BASIC_AUTH + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + credentials: + clientId: client1.apps.googleusercontent.com + cookieNames: + bearerToken: AccessToken-b0a1b740 + idToken: IdToken-b0a1b740 + oauthExpires: OauthExpires-b0a1b740 + oauthHmac: OauthHMAC-b0a1b740 + oauthNonce: OauthNonce-b0a1b740 + refreshToken: RefreshToken-b0a1b740 + hmacSecret: + name: oauth2/hmac_secret/securitypolicy/envoy-gateway/policy-for-gateway + sdsConfig: + ads: {} + resourceApiVersion: V3 + tokenSecret: + name: oauth2/client_secret/securitypolicy/envoy-gateway/policy-for-gateway + sdsConfig: + ads: {} + resourceApiVersion: V3 + defaultExpiresIn: 1800s + defaultRefreshTokenExpiresIn: 86400s + forwardBearerToken: true + redirectPathMatcher: + path: + exact: /bar/oauth2/callback + redirectUri: https://www.example.com/bar/oauth2/callback + retryPolicy: + numRetries: 3 + retryBackOff: + baseInterval: 1s + maxInterval: 5s + retryOn: 5xx,gateway-error,reset + signoutPath: + path: + exact: /bar/logout + tokenEndpoint: + cluster: securitypolicy/envoy-gateway/policy-for-gateway/0 + timeout: 10s + uri: https://oauth.foo.com/token + useRefreshToken: true + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http-10080 + useRemoteAddress: true + name: first-listener + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.routes.yaml new file mode 100644 index 00000000000..b17df86476d --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.routes.yaml @@ -0,0 +1,18 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: baz + name: first-route + route: + cluster: third-route-dest + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.oauth2/securitypolicy/envoy-gateway/policy-for-gateway: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.secrets.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.secrets.yaml new file mode 100644 index 00000000000..398ab6cef7b --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc-backencluster-provider.secrets.yaml @@ -0,0 +1,8 @@ +- genericSecret: + secret: + inlineBytes: Y2xpZW50MTpzZWNyZXQK + name: oauth2/client_secret/securitypolicy/envoy-gateway/policy-for-gateway +- genericSecret: + secret: + inlineBytes: qrOYACHXoe7UEDI/raOjNSx+Z9ufXSc/22C3T6X/zPY= + name: oauth2/hmac_secret/securitypolicy/envoy-gateway/policy-for-gateway diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.clusters.yaml new file mode 100644 index 00000000000..863e761bf9a --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.clusters.yaml @@ -0,0 +1,44 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: securitypolicy/envoy-gateway/policy-for-gateway/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: oauth.foo.com + portValue: 443 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: securitypolicy/envoy-gateway/policy-for-gateway/0/backend/0 + name: securitypolicy/envoy-gateway/policy-for-gateway/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + type: STRICT_DNS diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.endpoints.yaml new file mode 100644 index 00000000000..6c69841c2ad --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.listeners.yaml new file mode 100644 index 00000000000..3addb294484 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.listeners.yaml @@ -0,0 +1,83 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - disabled: true + name: envoy.filters.http.oauth2/securitypolicy/envoy-gateway/policy-for-gateway + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2 + config: + authScopes: + - openid + authType: BASIC_AUTH + authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth + credentials: + clientId: client1.apps.googleusercontent.com + cookieNames: + bearerToken: AccessToken-b0a1b740 + idToken: IdToken-b0a1b740 + oauthExpires: OauthExpires-b0a1b740 + oauthHmac: OauthHMAC-b0a1b740 + oauthNonce: OauthNonce-b0a1b740 + refreshToken: RefreshToken-b0a1b740 + hmacSecret: + name: oauth2/hmac_secret/securitypolicy/envoy-gateway/policy-for-gateway + sdsConfig: + ads: {} + resourceApiVersion: V3 + tokenSecret: + name: oauth2/client_secret/securitypolicy/envoy-gateway/policy-for-gateway + sdsConfig: + ads: {} + resourceApiVersion: V3 + defaultExpiresIn: 1800s + defaultRefreshTokenExpiresIn: 86400s + forwardBearerToken: true + redirectPathMatcher: + path: + exact: /bar/oauth2/callback + redirectUri: https://www.example.com/bar/oauth2/callback + retryPolicy: + numRetries: 3 + retryBackOff: + baseInterval: 1s + maxInterval: 5s + retryOn: 5xx,gateway-error,reset + signoutPath: + path: + exact: /bar/logout + tokenEndpoint: + cluster: securitypolicy/envoy-gateway/policy-for-gateway/0 + timeout: 10s + uri: https://oauth.foo.com/token + useRefreshToken: true + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http-10080 + useRemoteAddress: true + name: first-listener + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.routes.yaml new file mode 100644 index 00000000000..b17df86476d --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.routes.yaml @@ -0,0 +1,18 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + path: baz + name: first-route + route: + cluster: third-route-dest + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.oauth2/securitypolicy/envoy-gateway/policy-for-gateway: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.secrets.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.secrets.yaml new file mode 100644 index 00000000000..398ab6cef7b --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/oidc-backend-cluster-provider.secrets.yaml @@ -0,0 +1,8 @@ +- genericSecret: + secret: + inlineBytes: Y2xpZW50MTpzZWNyZXQK + name: oauth2/client_secret/securitypolicy/envoy-gateway/policy-for-gateway +- genericSecret: + secret: + inlineBytes: qrOYACHXoe7UEDI/raOjNSx+Z9ufXSc/22C3T6X/zPY= + name: oauth2/hmac_secret/securitypolicy/envoy-gateway/policy-for-gateway diff --git a/test/cel-validation/securitypolicy_test.go b/test/cel-validation/securitypolicy_test.go index c4efa812d58..f00ee84260c 100644 --- a/test/cel-validation/securitypolicy_test.go +++ b/test/cel-validation/securitypolicy_test.go @@ -18,6 +18,7 @@ import ( "k8s.io/utils/ptr" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) @@ -1121,6 +1122,103 @@ func TestSecurityPolicyTarget(t *testing.T) { }, wantErrors: []string{"at least one of claims or scopes must be specified"}, }, + { + desc: "oidc-retry", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetSelectors: []egv1a1.TargetSelector{ + { + Group: ptr.To(gwapiv1a2.Group("gateway.networking.k8s.io")), + Kind: "HTTPRoute", + MatchLabels: map[string]string{ + "eg/namespace": "reference-apps", + }, + }, + }, + }, + OIDC: &egv1a1.OIDC{ + Provider: egv1a1.OIDCProvider{ + BackendCluster: egv1a1.BackendCluster{ + BackendSettings: &egv1a1.ClusterSettings{ + Retry: &egv1a1.Retry{ + NumRetries: ptr.To(int32(3)), + PerRetry: &egv1a1.PerRetryPolicy{ + BackOff: &egv1a1.BackOffPolicy{ + BaseInterval: &metav1.Duration{ + Duration: time.Second * 1, + }, + MaxInterval: &metav1.Duration{ + Duration: time.Second * 10, + }, + }, + }, + RetryOn: &egv1a1.RetryOn{ + Triggers: []egv1a1.TriggerEnum{ + egv1a1.Error5XX, egv1a1.GatewayError, egv1a1.Reset, + }, + }, + }, + }, + }, + Issuer: "https://accounts.google.com", + AuthorizationEndpoint: ptr.To("https://accounts.google.com/o/oauth2/v2/auth"), + TokenEndpoint: ptr.To("https://oauth2.googleapis.com/token"), + }, + ClientID: "client-id", + ClientSecret: gwapiv1b1.SecretObjectReference{ + Name: "secret", + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "oidc-retry-unsupported-parameters", + mutate: func(sp *egv1a1.SecurityPolicy) { + sp.Spec = egv1a1.SecurityPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetSelectors: []egv1a1.TargetSelector{ + { + Group: ptr.To(gwapiv1a2.Group("gateway.networking.k8s.io")), + Kind: "HTTPRoute", + MatchLabels: map[string]string{ + "eg/namespace": "reference-apps", + }, + }, + }, + }, + OIDC: &egv1a1.OIDC{ + Provider: egv1a1.OIDCProvider{ + BackendCluster: egv1a1.BackendCluster{ + BackendSettings: &egv1a1.ClusterSettings{ + Retry: &egv1a1.Retry{ + NumRetries: ptr.To(int32(3)), + PerRetry: &egv1a1.PerRetryPolicy{ + Timeout: &metav1.Duration{ + Duration: time.Second * 10, + }, + }, + RetryOn: &egv1a1.RetryOn{ + HTTPStatusCodes: []egv1a1.HTTPStatus{500}, + }, + }, + }, + }, + Issuer: "https://accounts.google.com", + AuthorizationEndpoint: ptr.To("https://accounts.google.com/o/oauth2/v2/auth"), + TokenEndpoint: ptr.To("https://oauth2.googleapis.com/token"), + }, + ClientID: "client-id", + ClientSecret: gwapiv1b1.SecretObjectReference{ + Name: "secret", + }, + }, + } + }, + wantErrors: []string{"Retry timeout is not supported", "HTTPStatusCodes is not supported"}, + }, } for _, tc := range cases { diff --git a/test/e2e/testdata/oidc-securitypolicy-backendcluster.yaml b/test/e2e/testdata/oidc-securitypolicy-backendcluster.yaml new file mode 100644 index 00000000000..2e1a86791ec --- /dev/null +++ b/test/e2e/testdata/oidc-securitypolicy-backendcluster.yaml @@ -0,0 +1,86 @@ +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: http-with-oidc + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + hostnames: ["www.example.com"] + rules: + - matches: + - path: + type: PathPrefix + value: /myapp # This is the path that will be protected by OIDC + backendRefs: + - name: infra-backend-v1 + port: 8080 +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: http-keycloak + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + hostnames: ["keycloak.gateway-conformance-infra"] + rules: + - backendRefs: + - name: keycloak + port: 80 +--- +apiVersion: v1 +kind: Secret +metadata: + namespace: gateway-conformance-infra + name: oidctest-secret +data: + client-secret: b2lkY3Rlc3QtY2xpZW50LXNlY3JldA== # base64 encoding of "oidctest-client-secret" +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: SecurityPolicy +metadata: + name: oidc-test # Test OIDC Provider represented by a backend cluster + namespace: gateway-conformance-infra +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: http-with-oidc + oidc: + provider: + backendRefs: + - group: gateway.envoyproxy.io + kind: Backend + name: backend-keycloak + port: 80 + backendSettings: + retry: + numRetries: 3 + perRetry: + backOff: + baseInterval: 1s + maxInterval: 5s + retryOn: + triggers: ["5xx", "gateway-error", "reset"] + issuer: "http://keycloak.gateway-conformance-infra/realms/master" + authorizationEndpoint: "http://keycloak.gateway-conformance-infra/realms/master/protocol/openid-connect/auth" + tokenEndpoint: "http://keycloak.gateway-conformance-infra/realms/master/protocol/openid-connect/token" + clientID: "oidctest" + clientSecret: + name: "oidctest-secret" + redirectURL: "http://www.example.com/myapp/oauth2/callback" + logoutPath: "/myapp/logout" +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: Backend +metadata: + name: backend-keycloak + namespace: gateway-conformance-infra +spec: + endpoints: + - fqdn: + hostname: 'keycloak.gateway-conformance-infra' + port: 80 diff --git a/test/e2e/tests/oidc-backendcluster.go b/test/e2e/tests/oidc-backendcluster.go new file mode 100644 index 00000000000..b2bcc93cecb --- /dev/null +++ b/test/e2e/tests/oidc-backendcluster.go @@ -0,0 +1,37 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build e2e +// +build e2e + +package tests + +import ( + "testing" + + "sigs.k8s.io/gateway-api/conformance/utils/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, OIDCBackendClusterTest) +} + +// OIDCTest tests OIDC authentication for an http route with OIDC configured. +// The http route points to an application to verify that OIDC authentication works on application/http path level. +var OIDCBackendClusterTest = suite.ConformanceTest{ + ShortName: "OIDC with BackendCluster", + Description: "Test OIDC authentication", + Manifests: []string{"testdata/oidc-keycloak.yaml", "testdata/oidc-securitypolicy-backendcluster.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("oidc provider represented by a BackendCluster", func(t *testing.T) { + // Add a function to dump current cluster status + t.Cleanup(func() { + CollectAndDump(t, suite.RestConfig) + }) + + testOIDC(t, suite) + }) + }, +} diff --git a/test/e2e/tests/oidc.go b/test/e2e/tests/oidc.go index 27fb79be9b3..f03512c1e27 100644 --- a/test/e2e/tests/oidc.go +++ b/test/e2e/tests/oidc.go @@ -31,8 +31,6 @@ import ( ) const ( - testURL = "http://www.example.com/myapp" - logoutURL = "http://www.example.com/myapp/logout" keyCloakLoginFormID = "kc-form-login" username = "oidcuser" password = "oidcpassword" @@ -49,100 +47,13 @@ var OIDCTest = suite.ConformanceTest{ Description: "Test OIDC authentication", Manifests: []string{"testdata/oidc-keycloak.yaml", "testdata/oidc-securitypolicy.yaml"}, Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { - t.Run("http route with oidc authentication", func(t *testing.T) { + t.Run("oidc provider represented by a URL", func(t *testing.T) { // Add a function to dump current cluster status t.Cleanup(func() { CollectAndDump(t, suite.RestConfig) }) - ns := "gateway-conformance-infra" - routeNN := types.NamespacedName{Name: "http-with-oidc", Namespace: ns} - gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} - gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) - - ancestorRef := gwapiv1a2.ParentReference{ - Group: gatewayapi.GroupPtr(gwapiv1.GroupName), - Kind: gatewayapi.KindPtr(resource.KindGateway), - Namespace: gatewayapi.NamespacePtr(gwNN.Namespace), - Name: gwapiv1.ObjectName(gwNN.Name), - } - SecurityPolicyMustBeAccepted(t, suite.Client, types.NamespacedName{Name: "oidc-test", Namespace: ns}, suite.ControllerName, ancestorRef) - podInitialized := corev1.PodCondition{Type: corev1.PodInitialized, Status: corev1.ConditionTrue} - - // Wait for the keycloak pod to be configured with the test user and client - WaitForPods(t, suite.Client, ns, map[string]string{"job-name": "setup-keycloak"}, corev1.PodSucceeded, podInitialized) - - // Initialize the test OIDC client that will keep track of the state of the OIDC login process - client, err := NewOIDCTestClient( - WithLoggingOptions(t.Log, true), - // Map the application and keycloak cluster DNS name to the gateway address - WithCustomAddressMappings(map[string]string{ - "www.example.com:80": gwAddr, - "keycloak.gateway-conformance-infra:80": gwAddr, - }), - ) - require.NoError(t, err) - - if err := wait.PollUntilContextTimeout(context.TODO(), time.Second, 5*time.Minute, true, - func(_ context.Context) (done bool, err error) { - tlog.Logf(t, "sending request to %s", testURL) - - // Send a request to the http route with OIDC configured. - // It will be redirected to the keycloak login page - res, err := client.Get(testURL, true) - require.NoError(t, err, "Failed to get the login page") - require.Equal(t, 200, res.StatusCode, "Expected 200 OK") - - // Parse the response body to get the URL where the login page would post the user-entered credentials - if err := client.ParseLoginForm(res.Body, keyCloakLoginFormID); err != nil { - tlog.Logf(t, "failed to parse login form: %v", err) - return false, nil - } - - t.Log("successfully parsed login form") - return true, nil - }); err != nil { - t.Errorf("failed to parse login form: %v", err) - } - - // Submit the login form to the IdP. - // This will authenticate and redirect back to the application - res, err := client.Login(map[string]string{"username": username, "password": password, "credentialId": ""}) - require.NoError(t, err, "Failed to login to the IdP") - - // Verify that we get the expected response from the application - body, err := io.ReadAll(res.Body) - require.NoError(t, err) - require.Equal(t, http.StatusOK, res.StatusCode) - require.Contains(t, string(body), "infra-backend-v1", "Expected response from the application") - - // Verify that we can access the application without logging in again - res, err = client.Get(testURL, false) - require.NoError(t, err) - require.Equal(t, http.StatusOK, res.StatusCode) - require.Contains(t, string(body), "infra-backend-v1", "Expected response from the application") - - // Verify that we can logout - // Note: OAuth2 filter just clears its cookies and does not log out from the IdP. - res, err = client.Get(logoutURL, false) - require.NoError(t, err) - require.Equal(t, http.StatusFound, res.StatusCode) - - // After logout, OAuth2 filter will redirect back to the root of the host, e.g, "www.example.com". - // Ideally, this should redirect to the application's root, e.g, "www.example.com/myapp", - // but Envoy OAuth2 filter does not support this yet. - require.Equal(t, "http://www.example.com/", res.Header.Get("Location"), "Expected redirect to the root of the host") - - // Verify that the oauth2 cookies have been deleted - var cookieDeleted bool - deletedCookies := res.Header.Values("Set-Cookie") - regx := regexp.MustCompile("^IdToken-.+=deleted.+") - for _, cookie := range deletedCookies { - if regx.Match([]byte(cookie)) { - cookieDeleted = true - } - } - require.True(t, cookieDeleted, "IdToken cookie not deleted") + testOIDC(t, suite) }) t.Run("http route without oidc authentication", func(t *testing.T) { @@ -185,3 +96,102 @@ var OIDCTest = suite.ConformanceTest{ }) }, } + +func testOIDC(t *testing.T, suite *suite.ConformanceTestSuite) { + var ( + testURL = "http://www.example.com/myapp" + logoutURL = "http://www.example.com/myapp/logout" + route = "http-with-oidc" + sp = "oidc-test" + ns = "gateway-conformance-infra" + ) + + routeNN := types.NamespacedName{Name: route, Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + ancestorRef := gwapiv1a2.ParentReference{ + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), + Kind: gatewayapi.KindPtr(resource.KindGateway), + Namespace: gatewayapi.NamespacePtr(gwNN.Namespace), + Name: gwapiv1.ObjectName(gwNN.Name), + } + SecurityPolicyMustBeAccepted(t, suite.Client, types.NamespacedName{Name: sp, Namespace: ns}, suite.ControllerName, ancestorRef) + + podInitialized := corev1.PodCondition{Type: corev1.PodInitialized, Status: corev1.ConditionTrue} + + // Wait for the keycloak pod to be configured with the test user and client + WaitForPods(t, suite.Client, ns, map[string]string{"job-name": "setup-keycloak"}, corev1.PodSucceeded, podInitialized) + + // Initialize the test OIDC client that will keep track of the state of the OIDC login process + client, err := NewOIDCTestClient( + WithLoggingOptions(t.Log, true), + // Map the application and keycloak cluster DNS name to the gateway address + WithCustomAddressMappings(map[string]string{ + "www.example.com:80": gwAddr, + "keycloak.gateway-conformance-infra:80": gwAddr, + }), + ) + require.NoError(t, err) + + if err := wait.PollUntilContextTimeout(context.TODO(), time.Second, 5*time.Minute, true, + func(_ context.Context) (done bool, err error) { + tlog.Logf(t, "sending request to %s", testURL) + + // Send a request to the http route with OIDC configured. + // It will be redirected to the keycloak login page + res, err := client.Get(testURL, true) + require.NoError(t, err, "Failed to get the login page") + require.Equal(t, 200, res.StatusCode, "Expected 200 OK") + + // Parse the response body to get the URL where the login page would post the user-entered credentials + if err := client.ParseLoginForm(res.Body, keyCloakLoginFormID); err != nil { + tlog.Logf(t, "failed to parse login form: %v", err) + return false, nil + } + + t.Log("successfully parsed login form") + return true, nil + }); err != nil { + t.Errorf("failed to parse login form: %v", err) + } + + // Submit the login form to the IdP. + // This will authenticate and redirect back to the application + res, err := client.Login(map[string]string{"username": username, "password": password, "credentialId": ""}) + require.NoError(t, err, "Failed to login to the IdP") + + // Verify that we get the expected response from the application + body, err := io.ReadAll(res.Body) + require.NoError(t, err) + require.Equal(t, http.StatusOK, res.StatusCode) + require.Contains(t, string(body), "infra-backend-v1", "Expected response from the application") + + // Verify that we can access the application without logging in again + res, err = client.Get(testURL, false) + require.NoError(t, err) + require.Equal(t, http.StatusOK, res.StatusCode) + require.Contains(t, string(body), "infra-backend-v1", "Expected response from the application") + + // Verify that we can logout + // Note: OAuth2 filter just clears its cookies and does not log out from the IdP. + res, err = client.Get(logoutURL, false) + require.NoError(t, err) + require.Equal(t, http.StatusFound, res.StatusCode) + + // After logout, OAuth2 filter will redirect back to the root of the host, e.g, "www.example.com". + // Ideally, this should redirect to the application's root, e.g, "www.example.com/myapp", + // but Envoy OAuth2 filter does not support this yet. + require.Equal(t, "http://www.example.com/", res.Header.Get("Location"), "Expected redirect to the root of the host") + + // Verify that the oauth2 cookies have been deleted + var cookieDeleted bool + deletedCookies := res.Header.Values("Set-Cookie") + regx := regexp.MustCompile("^IdToken-.+=deleted.+") + for _, cookie := range deletedCookies { + if regx.Match([]byte(cookie)) { + cookieDeleted = true + } + } + require.True(t, cookieDeleted, "IdToken cookie not deleted") +} From 5a1c065e3ddbc12b6a232b92eaf3417cd3146a09 Mon Sep 17 00:00:00 2001 From: zirain Date: Fri, 18 Oct 2024 11:59:03 +0800 Subject: [PATCH 31/56] support reloadable EnvoyGateway configuration (#4451) * support reloadable EnvoyGateway configuration Signed-off-by: zirain * lint Signed-off-by: zirain * shutdown wasm http server Signed-off-by: zirain --------- Signed-off-by: zirain --- internal/cmd/server.go | 33 +- .../config/loader/configloader.go | 113 ++++++ .../config/loader/configloader_test.go | 59 ++++ .../config/loader/testdata/default.yaml | 24 ++ .../config/loader/testdata/enable-redis.yaml | 14 + internal/filewatcher/filewatcher.go | 179 ++++++++++ internal/filewatcher/filewatcher_test.go | 321 ++++++++++++++++++ internal/filewatcher/worker.go | 256 ++++++++++++++ internal/infrastructure/runner/runner.go | 7 + internal/provider/kubernetes/controller.go | 6 +- internal/provider/kubernetes/kubernetes.go | 3 +- internal/wasm/httpserver.go | 6 + tools/hack/deployment-exists.sh | 3 +- tools/make/kube.mk | 3 - 14 files changed, 1010 insertions(+), 17 deletions(-) create mode 100644 internal/envoygateway/config/loader/configloader.go create mode 100644 internal/envoygateway/config/loader/configloader_test.go create mode 100644 internal/envoygateway/config/loader/testdata/default.yaml create mode 100644 internal/envoygateway/config/loader/testdata/enable-redis.yaml create mode 100644 internal/filewatcher/filewatcher.go create mode 100644 internal/filewatcher/filewatcher_test.go create mode 100644 internal/filewatcher/worker.go diff --git a/internal/cmd/server.go b/internal/cmd/server.go index 25add4c8541..a4c9d3e9713 100644 --- a/internal/cmd/server.go +++ b/internal/cmd/server.go @@ -6,12 +6,15 @@ package cmd import ( + "context" + "github.com/spf13/cobra" ctrl "sigs.k8s.io/controller-runtime" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/admin" "github.com/envoyproxy/gateway/internal/envoygateway/config" + "github.com/envoyproxy/gateway/internal/envoygateway/config/loader" extensionregistry "github.com/envoyproxy/gateway/internal/extension/registry" "github.com/envoyproxy/gateway/internal/extension/types" gatewayapirunner "github.com/envoyproxy/gateway/internal/gatewayapi/runner" @@ -51,6 +54,20 @@ func server() error { return err } + ctx := ctrl.SetupSignalHandler() + hook := func(c context.Context, cfg *config.Server) error { + cfg.Logger.Info("Setup runners") + if err := setupRunners(c, cfg); err != nil { + cfg.Logger.Error(err, "failed to setup runners") + return err + } + return nil + } + l := loader.New(cfgPath, cfg, hook) + if err := l.Start(ctx); err != nil { + return err + } + // Init eg admin servers. if err := admin.Init(cfg); err != nil { return err @@ -60,10 +77,10 @@ func server() error { return err } - // init eg runners. - if err := setupRunners(cfg); err != nil { - return err - } + // Wait exit signal + <-ctx.Done() + + cfg.Logger.Info("shutting down") return nil } @@ -110,11 +127,7 @@ func getConfigByPath(cfgPath string) (*config.Server, error) { // setupRunners starts all the runners required for the Envoy Gateway to // fulfill its tasks. -func setupRunners(cfg *config.Server) (err error) { - // TODO - Setup a Config Manager - // https://github.com/envoyproxy/gateway/issues/43 - ctx := ctrl.SetupSignalHandler() - +func setupRunners(ctx context.Context, cfg *config.Server) (err error) { // Setup the Extension Manager var extMgr types.Manager if cfg.EnvoyGateway.Provider.Type == egv1a1.ProviderTypeKubernetes { @@ -212,7 +225,7 @@ func setupRunners(cfg *config.Server) (err error) { infraIR.Close() xds.Close() - cfg.Logger.Info("shutting down") + cfg.Logger.Info("runners are shutting down") if extMgr != nil { // Close connections to extension services diff --git a/internal/envoygateway/config/loader/configloader.go b/internal/envoygateway/config/loader/configloader.go new file mode 100644 index 00000000000..9523c7a432e --- /dev/null +++ b/internal/envoygateway/config/loader/configloader.go @@ -0,0 +1,113 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package loader + +import ( + "context" + "time" + + "github.com/envoyproxy/gateway/internal/envoygateway/config" + "github.com/envoyproxy/gateway/internal/filewatcher" + "github.com/envoyproxy/gateway/internal/logging" +) + +type HookFunc func(c context.Context, cfg *config.Server) error + +type Loader struct { + cfgPath string + cfg *config.Server + logger logging.Logger + cancel context.CancelFunc + hook HookFunc + + w filewatcher.FileWatcher +} + +func New(cfgPath string, cfg *config.Server, f HookFunc) *Loader { + return &Loader{ + cfgPath: cfgPath, + cfg: cfg, + logger: cfg.Logger.WithName("config-loader"), + hook: f, + w: filewatcher.NewWatcher(), + } +} + +func (r *Loader) Start(ctx context.Context) error { + r.runHook() + + if r.cfgPath == "" { + r.logger.Info("no config file provided, skipping config watcher") + return nil + } + + r.logger.Info("watching for changes to the EnvoyGateway configuration", "path", r.cfgPath) + if err := r.w.Add(r.cfgPath); err != nil { + r.logger.Error(err, "failed to add config file to watcher") + return err + } + + go func() { + defer func() { + _ = r.w.Close() + }() + for { + select { + case e := <-r.w.Events(r.cfgPath): + r.logger.Info("received fsnotify events", "name", e.Name, "op", e.Op.String()) + + // Load the config file. + eg, err := config.Decode(r.cfgPath) + if err != nil { + r.logger.Info("failed to decode config file", "name", r.cfgPath, "error", err) + // TODO: add a metric for this? + continue + } + // Set defaults for unset fields + eg.SetEnvoyGatewayDefaults() + r.cfg.EnvoyGateway = eg + // update cfg logger + eg.Logging.SetEnvoyGatewayLoggingDefaults() + r.cfg.Logger = logging.NewLogger(eg.Logging) + + // cancel last + if r.cancel != nil { + r.cancel() + } + + // TODO: we need to make sure that all runners are stopped, before we start the new ones + // Otherwise we might end up with error listening on:8081 + time.Sleep(3 * time.Second) + + r.runHook() + case err := <-r.w.Errors(r.cfgPath): + r.logger.Error(err, "watcher error") + case <-ctx.Done(): + if r.cancel != nil { + r.cancel() + } + return + } + } + }() + + return nil +} + +func (r *Loader) runHook() { + if r.hook == nil { + return + } + + r.logger.Info("running hook") + c, cancel := context.WithCancel(context.TODO()) + r.cancel = cancel + go func(ctx context.Context) { + if err := r.hook(ctx, r.cfg); err != nil { + r.logger.Error(err, "hook error") + } + }(c) +} diff --git a/internal/envoygateway/config/loader/configloader_test.go b/internal/envoygateway/config/loader/configloader_test.go new file mode 100644 index 00000000000..d0420df0f5f --- /dev/null +++ b/internal/envoygateway/config/loader/configloader_test.go @@ -0,0 +1,59 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package loader + +import ( + "context" + _ "embed" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/envoyproxy/gateway/internal/envoygateway/config" +) + +var ( + //go:embed testdata/default.yaml + defaultConfig string + //go:embed testdata/enable-redis.yaml + redisConfig string +) + +func TestConfigLoader(t *testing.T) { + tmpDir, err := os.MkdirTemp("", "envoy-gateway-configloader-test") + require.NoError(t, err) + defer func(path string) { + _ = os.RemoveAll(path) + }(tmpDir) + + cfgPath := tmpDir + "/config.yaml" + require.NoError(t, os.WriteFile(cfgPath, []byte(defaultConfig), 0o600)) + s, err := config.New() + require.NoError(t, err) + + ctx, cancel := context.WithCancel(context.TODO()) + defer func() { + cancel() + }() + + changed := 0 + loader := New(cfgPath, s, func(_ context.Context, cfg *config.Server) error { + changed++ + t.Logf("config changed %d times", changed) + if changed > 1 { + cancel() + } + return nil + }) + + require.NoError(t, loader.Start(ctx)) + go func() { + _ = os.WriteFile(cfgPath, []byte(redisConfig), 0o600) + }() + + <-ctx.Done() +} diff --git a/internal/envoygateway/config/loader/testdata/default.yaml b/internal/envoygateway/config/loader/testdata/default.yaml new file mode 100644 index 00000000000..20463f848e1 --- /dev/null +++ b/internal/envoygateway/config/loader/testdata/default.yaml @@ -0,0 +1,24 @@ +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +gateway: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +logging: + level: + default: info +provider: + kubernetes: + rateLimitDeployment: + container: + image: docker.io/envoyproxy/ratelimit:master + patch: + type: StrategicMerge + value: + spec: + template: + spec: + containers: + - imagePullPolicy: IfNotPresent + name: envoy-ratelimit + shutdownManager: + image: docker.io/envoyproxy/gateway-dev:latest + type: Kubernetes diff --git a/internal/envoygateway/config/loader/testdata/enable-redis.yaml b/internal/envoygateway/config/loader/testdata/enable-redis.yaml new file mode 100644 index 00000000000..ed2218ab5ab --- /dev/null +++ b/internal/envoygateway/config/loader/testdata/enable-redis.yaml @@ -0,0 +1,14 @@ +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyGateway +provider: + type: Kubernetes +gateway: + controllerName: gateway.envoyproxy.io/gatewayclass-controller +extensionApis: + enableEnvoyPatchPolicy: true + enableBackend: true +rateLimit: + backend: + type: Redis + redis: + url: redis.redis-system.svc.cluster.local:6379 diff --git a/internal/filewatcher/filewatcher.go b/internal/filewatcher/filewatcher.go new file mode 100644 index 00000000000..4fce5e9aba4 --- /dev/null +++ b/internal/filewatcher/filewatcher.go @@ -0,0 +1,179 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package filewatcher + +import ( + "errors" + "fmt" + "path/filepath" + "sync" + + "github.com/fsnotify/fsnotify" +) + +// FileWatcher is an interface that watches a set of files, +// delivering events to related channel. +type FileWatcher interface { + Add(path string) error + Remove(path string) error + Close() error + Events(path string) chan fsnotify.Event + Errors(path string) chan error +} + +type fileWatcher struct { + mu sync.RWMutex + + // The watcher maintain a map of workers, + // keyed by watched dir (parent dir of watched files). + workers map[string]*workerState + + funcs *patchTable +} + +type workerState struct { + worker *worker + count int +} + +// functions that can be replaced in a test setting +type patchTable struct { + newWatcher func() (*fsnotify.Watcher, error) + addWatcherPath func(*fsnotify.Watcher, string) error +} + +// NewWatcher return with a FileWatcher instance that implemented with fsnotify. +func NewWatcher() FileWatcher { + return &fileWatcher{ + workers: map[string]*workerState{}, + + // replaceable functions for tests + funcs: &patchTable{ + newWatcher: fsnotify.NewWatcher, + addWatcherPath: func(watcher *fsnotify.Watcher, path string) error { + return watcher.Add(path) + }, + }, + } +} + +// Close releases all resources associated with the watcher +func (fw *fileWatcher) Close() error { + fw.mu.Lock() + defer fw.mu.Unlock() + + for _, ws := range fw.workers { + ws.worker.terminate() + } + fw.workers = nil + + return nil +} + +// Add a path to watch +func (fw *fileWatcher) Add(path string) error { + fw.mu.Lock() + defer fw.mu.Unlock() + + ws, cleanedPath, _, err := fw.getWorker(path) + if err != nil { + return err + } + + if err = ws.worker.addPath(cleanedPath); err == nil { + ws.count++ + } + + return err +} + +// Stop watching a path +func (fw *fileWatcher) Remove(path string) error { + fw.mu.Lock() + defer fw.mu.Unlock() + + ws, cleanedPath, parentPath, err := fw.getWorker(path) + if err != nil { + return err + } + + if err = ws.worker.removePath(cleanedPath); err == nil { + ws.count-- + if ws.count == 0 { + ws.worker.terminate() + delete(fw.workers, parentPath) + } + } + + return err +} + +// Events returns an event notification channel for a path +func (fw *fileWatcher) Events(path string) chan fsnotify.Event { + fw.mu.RLock() + defer fw.mu.RUnlock() + + ws, cleanedPath, err := fw.findWorker(path) + if err != nil { + return nil + } + + return ws.worker.eventChannel(cleanedPath) +} + +// Errors returns an error notification channel for a path +func (fw *fileWatcher) Errors(path string) chan error { + fw.mu.RLock() + defer fw.mu.RUnlock() + + ws, cleanedPath, err := fw.findWorker(path) + if err != nil { + return nil + } + + return ws.worker.errorChannel(cleanedPath) +} + +func (fw *fileWatcher) getWorker(path string) (*workerState, string, string, error) { + if fw.workers == nil { + return nil, "", "", errors.New("using a closed watcher") + } + + cleanedPath := filepath.Clean(path) + parentPath, _ := filepath.Split(cleanedPath) + + ws, workerExists := fw.workers[parentPath] + if !workerExists { + wk, err := newWorker(parentPath, fw.funcs) + if err != nil { + return nil, "", "", err + } + + ws = &workerState{ + worker: wk, + } + + fw.workers[parentPath] = ws + } + + return ws, cleanedPath, parentPath, nil +} + +func (fw *fileWatcher) findWorker(path string) (*workerState, string, error) { + if fw.workers == nil { + return nil, "", errors.New("using a closed watcher") + } + + cleanedPath := filepath.Clean(path) + parentPath, _ := filepath.Split(cleanedPath) + + ws, workerExists := fw.workers[parentPath] + if !workerExists { + return nil, "", fmt.Errorf("no path registered for %s", path) + } + + return ws, cleanedPath, nil +} diff --git a/internal/filewatcher/filewatcher_test.go b/internal/filewatcher/filewatcher_test.go new file mode 100644 index 00000000000..5230d7c05ad --- /dev/null +++ b/internal/filewatcher/filewatcher_test.go @@ -0,0 +1,321 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package filewatcher + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path" + "runtime" + "sync" + "testing" + + "github.com/fsnotify/fsnotify" + "github.com/stretchr/testify/require" +) + +func newWatchFile(t *testing.T) string { + watchDir := t.TempDir() + watchFile := path.Join(watchDir, "test.conf") + err := os.WriteFile(watchFile, []byte("foo: bar\n"), 0o600) + require.NoError(t, err) + + return watchFile +} + +func newWatchFileThatDoesNotExist(t *testing.T) string { + watchDir := t.TempDir() + + watchFile := path.Join(watchDir, "test.conf") + + return watchFile +} + +// newTwoWatchFile returns with two watch files that exist in the same base dir. +func newTwoWatchFile(t *testing.T) (string, string) { + watchDir := t.TempDir() + + watchFile1 := path.Join(watchDir, "test1.conf") + err := os.WriteFile(watchFile1, []byte("foo: bar\n"), 0o600) + require.NoError(t, err) + + watchFile2 := path.Join(watchDir, "test2.conf") + err = os.WriteFile(watchFile2, []byte("foo: baz\n"), 0o600) + require.NoError(t, err) + + return watchFile1, watchFile2 +} + +// newSymlinkedWatchFile simulates the behavior of k8s configmap/secret. +// Path structure looks like: +// +// /test.conf +// ^ +// | +// +// /data/test.conf +// +// ^ +// | +// +// /data1/test.conf +func newSymlinkedWatchFile(t *testing.T) (string, string) { + watchDir := t.TempDir() + + dataDir1 := path.Join(watchDir, "data1") + err := os.Mkdir(dataDir1, 0o777) + require.NoError(t, err) + + realTestFile := path.Join(dataDir1, "test.conf") + t.Logf("Real test file location: %s\n", realTestFile) + err = os.WriteFile(realTestFile, []byte("foo: bar\n"), 0o600) + require.NoError(t, err) + + // Now, symlink the tmp `data1` dir to `data` in the baseDir + require.NoError(t, os.Symlink(dataDir1, path.Join(watchDir, "data"))) + // And link the `/datadir/test.conf` to `/test.conf` + watchFile := path.Join(watchDir, "test.conf") + require.NoError(t, os.Symlink(path.Join(watchDir, "data", "test.conf"), watchFile)) + t.Logf("Watch file location: %s\n", path.Join(watchDir, "test.conf")) + return watchDir, watchFile +} + +func TestWatchFile(t *testing.T) { + t.Run("file content changed", func(t *testing.T) { + // Given a file being watched + watchFile := newWatchFile(t) + _, err := os.Stat(watchFile) + require.NoError(t, err) + + w := NewWatcher() + require.NoError(t, w.Add(watchFile)) + events := w.Events(watchFile) + + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + <-events + wg.Done() + }() + + // Overwriting the file and waiting its event to be received. + err = os.WriteFile(watchFile, []byte("foo: baz\n"), 0o600) + require.NoError(t, err) + wg.Wait() + + _ = w.Close() + }) + + t.Run("link to real file changed (for k8s configmap/secret path)", func(t *testing.T) { + // skip if not executed on Linux + if runtime.GOOS != "linux" { + t.Skip("Skipping test as symlink replacements don't work on non-linux environment...") + } + + watchDir, watchFile := newSymlinkedWatchFile(t) + + w := NewWatcher() + require.NoError(t, w.Add(watchFile)) + events := w.Events(watchFile) + + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + <-events + wg.Done() + }() + + // Link to another `test.conf` file + dataDir2 := path.Join(watchDir, "data2") + err := os.Mkdir(dataDir2, 0o777) + require.NoError(t, err) + + watchFile2 := path.Join(dataDir2, "test.conf") + err = os.WriteFile(watchFile2, []byte("foo: baz\n"), 0o600) + require.NoError(t, err) + + // change the symlink using the `ln -sfn` command + err = exec.Command("ln", "-sfn", dataDir2, path.Join(watchDir, "data")).Run() + require.NoError(t, err) + + // Wait its event to be received. + wg.Wait() + + _ = w.Close() + }) + + t.Run("file added later", func(t *testing.T) { + // Given a file being watched + watchFile := newWatchFileThatDoesNotExist(t) + + w := NewWatcher() + require.NoError(t, w.Add(watchFile)) + events := w.Events(watchFile) + + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + <-events + wg.Done() + }() + + // Overwriting the file and waiting its event to be received. + err := os.WriteFile(watchFile, []byte("foo: baz\n"), 0o600) + require.NoError(t, err) + wg.Wait() + + _ = w.Close() + }) +} + +func TestWatcherLifecycle(t *testing.T) { + watchFile1, watchFile2 := newTwoWatchFile(t) + + w := NewWatcher() + + // Validate Add behavior + err := w.Add(watchFile1) + require.NoError(t, err) + err = w.Add(watchFile2) + require.NoError(t, err) + + // Validate events and errors channel are fulfilled. + events1 := w.Events(watchFile1) + require.NotNil(t, events1) + events2 := w.Events(watchFile2) + require.NotNil(t, events2) + + errors1 := w.Errors(watchFile1) + require.NotNil(t, errors1) + errors2 := w.Errors(watchFile2) + require.NotNil(t, errors2) + + // Validate Remove behavior + err = w.Remove(watchFile1) + require.NoError(t, err) + events1 = w.Events(watchFile1) + require.Nil(t, events1) + errors1 = w.Errors(watchFile1) + require.Nil(t, errors1) + events2 = w.Events(watchFile2) + require.NotNil(t, events2) + errors2 = w.Errors(watchFile2) + require.NotNil(t, errors2) + + fmt.Printf("2\n") + // Validate Close behavior + err = w.Close() + require.NoError(t, err) + events1 = w.Events(watchFile1) + require.Nil(t, events1) + errors1 = w.Errors(watchFile1) + require.Nil(t, errors1) + events2 = w.Events(watchFile2) + require.Nil(t, events2) + errors2 = w.Errors(watchFile2) + require.Nil(t, errors2) +} + +func TestErrors(t *testing.T) { + w := NewWatcher() + + if ch := w.Errors("XYZ"); ch != nil { + t.Error("Expected no channel") + } + + if ch := w.Events("XYZ"); ch != nil { + t.Error("Expected no channel") + } + + name := newWatchFile(t) + _ = w.Add(name) + _ = w.Remove(name) + + if ch := w.Errors("XYZ"); ch != nil { + t.Error("Expected no channel") + } + + if ch := w.Events(name); ch != nil { + t.Error("Expected no channel") + } + + _ = w.Close() + + if err := w.Add(name); err == nil { + t.Error("Expecting error") + } + + if err := w.Remove(name); err == nil { + t.Error("Expecting error") + } + + if ch := w.Errors(name); ch != nil { + t.Error("Expecting nil") + } + + if ch := w.Events(name); ch != nil { + t.Error("Expecting nil") + } +} + +func TestBadWatcher(t *testing.T) { + w := NewWatcher() + w.(*fileWatcher).funcs.newWatcher = func() (*fsnotify.Watcher, error) { + return nil, errors.New("FOOBAR") + } + + name := newWatchFile(t) + if err := w.Add(name); err == nil { + t.Errorf("Expecting error, got nil") + } + if err := w.Close(); err != nil { + t.Errorf("Expecting nil, got %v", err) + } +} + +func TestBadAddWatcher(t *testing.T) { + w := NewWatcher() + w.(*fileWatcher).funcs.addWatcherPath = func(*fsnotify.Watcher, string) error { + return errors.New("FOOBAR") + } + + name := newWatchFile(t) + if err := w.Add(name); err == nil { + t.Errorf("Expecting error, got nil") + } + if err := w.Close(); err != nil { + t.Errorf("Expecting nil, got %v", err) + } +} + +func TestDuplicateAdd(t *testing.T) { + w := NewWatcher() + + name := newWatchFile(t) + + if err := w.Add(name); err != nil { + t.Errorf("Expecting nil, got %v", err) + } + + if err := w.Add(name); err == nil { + t.Errorf("Expecting error, got nil") + } + + _ = w.Close() +} + +func TestBogusRemove(t *testing.T) { + w := NewWatcher() + + name := newWatchFile(t) + if err := w.Remove(name); err == nil { + t.Errorf("Expecting error, got nil") + } + + _ = w.Close() +} diff --git a/internal/filewatcher/worker.go b/internal/filewatcher/worker.go new file mode 100644 index 00000000000..6ae9c9f77ba --- /dev/null +++ b/internal/filewatcher/worker.go @@ -0,0 +1,256 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package filewatcher + +import ( + "bufio" + "bytes" + "crypto/sha256" + "fmt" + "io" + "os" + "sync" + + "github.com/fsnotify/fsnotify" +) + +type worker struct { + mu sync.RWMutex + + // watcher is an fsnotify watcher that watches the parent + // dir of watchedFiles. + dirWatcher *fsnotify.Watcher + + // The worker maintains a map of channels keyed by watched file path. + // The worker watches parent path of given path, + // and filters out events of given path, then redirect + // to the result channel. + // Note that for symlink files, the content in received events + // do not have to be related to the file itself. + watchedFiles map[string]*fileTracker + + // tracker lifecycle + retireTrackerCh chan *fileTracker + + // tells the worker to exit + terminateCh chan bool +} + +type fileTracker struct { + events chan fsnotify.Event + errors chan error + + // Hash sum to indicate if a file has been updated. + hash []byte +} + +func newWorker(path string, funcs *patchTable) (*worker, error) { + dirWatcher, err := funcs.newWatcher() + if err != nil { + return nil, err + } + + if err = funcs.addWatcherPath(dirWatcher, path); err != nil { + _ = dirWatcher.Close() + return nil, err + } + + wk := &worker{ + dirWatcher: dirWatcher, + watchedFiles: make(map[string]*fileTracker), + retireTrackerCh: make(chan *fileTracker), + terminateCh: make(chan bool), + } + + go wk.listen() + + return wk, nil +} + +func (wk *worker) listen() { + wk.loop() + + _ = wk.dirWatcher.Close() + + // drain any retiring trackers that may be pending + wk.drainRetiringTrackers() + + // clean up the rest + for _, ft := range wk.watchedFiles { + retireTracker(ft) + } +} + +func (wk *worker) loop() { + for { + select { + case event := <-wk.dirWatcher.Events: + // work on a copy of the watchedFiles map, so that we don't interfere + // with the caller's use of the map + for path, ft := range wk.getTrackers() { + if ft.events == nil { + // tracker has been retired, skip it + continue + } + + sum := getHashSum(path) + if !bytes.Equal(sum, ft.hash) { + ft.hash = sum + + select { + case ft.events <- event: + // nothing to do + + case ft := <-wk.retireTrackerCh: + retireTracker(ft) + + case <-wk.terminateCh: + return + } + } + } + + case err := <-wk.dirWatcher.Errors: + for _, ft := range wk.getTrackers() { + if ft.errors == nil { + // tracker has been retired, skip it + continue + } + + select { + case ft.errors <- err: + // nothing to do + + case ft := <-wk.retireTrackerCh: + retireTracker(ft) + + case <-wk.terminateCh: + return + } + } + + case ft := <-wk.retireTrackerCh: + retireTracker(ft) + + case <-wk.terminateCh: + return + } + } +} + +// used only by the worker goroutine +func (wk *worker) drainRetiringTrackers() { + // cleanup any trackers that were in the process + // of being retired, but didn't get processed due + // to termination + for { + select { + case ft := <-wk.retireTrackerCh: + retireTracker(ft) + default: + return + } + } +} + +// make a local copy of the set of trackers to avoid contention with callers +// used only by the worker goroutine +func (wk *worker) getTrackers() map[string]*fileTracker { + wk.mu.RLock() + + result := make(map[string]*fileTracker, len(wk.watchedFiles)) + for k, v := range wk.watchedFiles { + result[k] = v + } + + wk.mu.RUnlock() + return result +} + +// used only by the worker goroutine +func retireTracker(ft *fileTracker) { + close(ft.events) + close(ft.errors) + ft.events = nil + ft.errors = nil +} + +func (wk *worker) terminate() { + wk.terminateCh <- true +} + +func (wk *worker) addPath(path string) error { + wk.mu.Lock() + + ft := wk.watchedFiles[path] + if ft != nil { + wk.mu.Unlock() + return fmt.Errorf("path %s is already being watched", path) + } + + ft = &fileTracker{ + events: make(chan fsnotify.Event), + errors: make(chan error), + hash: getHashSum(path), + } + + wk.watchedFiles[path] = ft + wk.mu.Unlock() + + return nil +} + +func (wk *worker) removePath(path string) error { + wk.mu.Lock() + + ft := wk.watchedFiles[path] + if ft == nil { + wk.mu.Unlock() + return fmt.Errorf("path %s not found", path) + } + + delete(wk.watchedFiles, path) + wk.mu.Unlock() + + wk.retireTrackerCh <- ft + return nil +} + +func (wk *worker) eventChannel(path string) chan fsnotify.Event { + wk.mu.RLock() + defer wk.mu.RUnlock() + + if ft := wk.watchedFiles[path]; ft != nil { + return ft.events + } + + return nil +} + +func (wk *worker) errorChannel(path string) chan error { + wk.mu.RLock() + defer wk.mu.RUnlock() + + if ft := wk.watchedFiles[path]; ft != nil { + return ft.errors + } + + return nil +} + +// gets the hash of the given file, or nil if there's a problem +func getHashSum(file string) []byte { + f, err := os.Open(file) + if err != nil { + return nil + } + defer f.Close() + r := bufio.NewReader(f) + + h := sha256.New() + _, _ = io.Copy(h, r) + return h.Sum(nil) +} diff --git a/internal/infrastructure/runner/runner.go b/internal/infrastructure/runner/runner.go index 6c261aff3f3..300314d92e0 100644 --- a/internal/infrastructure/runner/runner.go +++ b/internal/infrastructure/runner/runner.go @@ -56,6 +56,13 @@ func (r *Runner) Start(ctx context.Context) (err error) { // Enable global ratelimit if it has been configured. if r.EnvoyGateway.RateLimit != nil { go r.enableRateLimitInfra(ctx) + } else { + // Delete the ratelimit infra if it exists. + go func() { + if err := r.mgr.DeleteRateLimitInfra(ctx); err != nil { + r.Logger.Error(err, "failed to delete ratelimit infra") + } + }() } r.Logger.Info("started") } diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 915e6e5acd8..167e70746ec 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -8,6 +8,7 @@ package kubernetes import ( "context" "fmt" + "time" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -111,7 +112,10 @@ func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su Updater r.namespaceLabel = cfg.EnvoyGateway.Provider.Kubernetes.Watch.NamespaceSelector } - c, err := controller.New("gatewayapi", mgr, controller.Options{Reconciler: r, SkipNameValidation: skipNameValidation()}) + // controller-runtime doesn't allow run controller with same name for more than once + // see https://github.com/kubernetes-sigs/controller-runtime/blob/2b941650bce159006c88bd3ca0d132c7bc40e947/pkg/controller/name.go#L29 + name := fmt.Sprintf("gatewayapi-%d", time.Now().Unix()) + c, err := controller.New(name, mgr, controller.Options{Reconciler: r, SkipNameValidation: skipNameValidation()}) if err != nil { return fmt.Errorf("error creating controller: %w", err) } diff --git a/internal/provider/kubernetes/kubernetes.go b/internal/provider/kubernetes/kubernetes.go index cca90a24a17..4fdbc329dd0 100644 --- a/internal/provider/kubernetes/kubernetes.go +++ b/internal/provider/kubernetes/kubernetes.go @@ -112,7 +112,8 @@ func New(cfg *rest.Config, svr *ec.Server, resources *message.ProviderResources) // Emit elected & continue with envoyObjects of infra resources go func() { <-mgr.Elected() - close(svr.Elected) + // WARN: DO NOT CLOSE IT + svr.Elected <- struct{}{} }() return &Provider{ diff --git a/internal/wasm/httpserver.go b/internal/wasm/httpserver.go index 9b1d0b32c90..14e70a8c6dc 100644 --- a/internal/wasm/httpserver.go +++ b/internal/wasm/httpserver.go @@ -131,6 +131,12 @@ func (s *HTTPServer) Start(ctx context.Context) { return } }() + + go func() { + // waiting for shutdown + <-ctx.Done() + _ = s.server.Shutdown(context.Background()) + }() s.cache.Start(ctx) go s.resetFailedAttempts(ctx) } diff --git a/tools/hack/deployment-exists.sh b/tools/hack/deployment-exists.sh index cc50e1c2638..d5081b424bc 100755 --- a/tools/hack/deployment-exists.sh +++ b/tools/hack/deployment-exists.sh @@ -3,9 +3,8 @@ DEPLOYMENT_LABEL_SELECTOR=$1 DEPLOYMENT_NAMESPACE=$2 - # Timeout for deployment to exist (in seconds) -exist_timeout=25 +exist_timeout=300 end=$((SECONDS+exist_timeout)) while true; do diff --git a/tools/make/kube.mk b/tools/make/kube.mk index c659e234787..91c1580ab2d 100644 --- a/tools/make/kube.mk +++ b/tools/make/kube.mk @@ -138,9 +138,6 @@ e2e: create-cluster kube-install-image kube-deploy install-ratelimit install-e2e install-ratelimit: @$(LOG_TARGET) kubectl apply -f examples/redis/redis.yaml - kubectl rollout restart deployment envoy-gateway -n envoy-gateway-system - kubectl rollout status --watch --timeout=5m -n envoy-gateway-system deployment/envoy-gateway - kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available tools/hack/deployment-exists.sh "app.kubernetes.io/name=envoy-ratelimit" "envoy-gateway-system" kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-ratelimit --for=condition=Available From 272da6dae0a558c52a3a974ca6cb90cc3e8fe84a Mon Sep 17 00:00:00 2001 From: Rudrakh Panigrahi Date: Sat, 19 Oct 2024 05:08:22 +0530 Subject: [PATCH 32/56] e2e: add tests for ratelimit invert matching headers (#4452) add tests for ratelimit invert matching headers Signed-off-by: Rudrakh Panigrahi --- .../ratelimit-header-invert-match.yaml | 42 +++++++++ test/e2e/tests/ratelimit.go | 88 +++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 test/e2e/testdata/ratelimit-header-invert-match.yaml diff --git a/test/e2e/testdata/ratelimit-header-invert-match.yaml b/test/e2e/testdata/ratelimit-header-invert-match.yaml new file mode 100644 index 00000000000..7261ef30e35 --- /dev/null +++ b/test/e2e/testdata/ratelimit-header-invert-match.yaml @@ -0,0 +1,42 @@ +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-anded-headers-with-invert + namespace: gateway-conformance-infra +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: header-ratelimit + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-name + type: Distinct + - name: x-user-name + type: Exact + value: admin + invert: true + limit: + requests: 3 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: header-ratelimit + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + rules: + - matches: + - path: + type: PathPrefix + value: /get + backendRefs: + - name: infra-backend-v1 + port: 8080 diff --git a/test/e2e/tests/ratelimit.go b/test/e2e/tests/ratelimit.go index 80064e6d906..b87576b60aa 100644 --- a/test/e2e/tests/ratelimit.go +++ b/test/e2e/tests/ratelimit.go @@ -26,6 +26,7 @@ import ( func init() { ConformanceTests = append(ConformanceTests, RateLimitCIDRMatchTest) ConformanceTests = append(ConformanceTests, RateLimitHeaderMatchTest) + ConformanceTests = append(ConformanceTests, RateLimitHeaderInvertMatchTest) ConformanceTests = append(ConformanceTests, RateLimitHeadersDisabled) ConformanceTests = append(ConformanceTests, RateLimitBasedJwtClaimsTest) ConformanceTests = append(ConformanceTests, RateLimitMultipleListenersTest) @@ -170,6 +171,93 @@ var RateLimitHeaderMatchTest = suite.ConformanceTest{ }, } +var RateLimitHeaderInvertMatchTest = suite.ConformanceTest{ + ShortName: "RateLimitHeaderInvertMatch", + Description: "Limit all requests that match distinct headers except for which invert is set to true", + Manifests: []string{"testdata/ratelimit-header-invert-match.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "header-ratelimit", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + t.Run("all matched headers got limited", func(t *testing.T) { + requestHeaders := map[string]string{ + "x-user-name": "username", + } + + ratelimitHeader := make(map[string]string) + expectOkResp := http.ExpectedResponse{ + Request: http.Request{ + Path: "/get", + Headers: requestHeaders, + }, + Response: http.Response{ + StatusCode: 200, + Headers: ratelimitHeader, + }, + Namespace: ns, + } + expectOkResp.Response.Headers["X-Ratelimit-Limit"] = "3, 3;w=3600" + expectOkReq := http.MakeRequest(t, &expectOkResp, gwAddr, "HTTP", "http") + + expectLimitResp := http.ExpectedResponse{ + Request: http.Request{ + Path: "/get", + Headers: requestHeaders, + }, + Response: http.Response{ + StatusCode: 429, + }, + Namespace: ns, + } + expectLimitReq := http.MakeRequest(t, &expectLimitResp, gwAddr, "HTTP", "http") + + // should just send exactly 4 requests, and expect 429 + + // keep sending requests till get 200 first, that will cost one 200 + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectOkResp) + + // fire the rest of the requests + if err := GotExactExpectedResponse(t, 2, suite.RoundTripper, expectOkReq, expectOkResp); err != nil { + t.Errorf("failed to get expected response for the first three requests: %v", err) + } + if err := GotExactExpectedResponse(t, 1, suite.RoundTripper, expectLimitReq, expectLimitResp); err != nil { + t.Errorf("failed to get expected response for the last (fourth) request: %v", err) + } + }) + + t.Run("if header matched with invert will not get limited", func(t *testing.T) { + requestHeaders := map[string]string{ + "x-user-name": "admin", + } + + // it does not require any rate limit header, since this request never be rate limited. + expectOkResp := http.ExpectedResponse{ + Request: http.Request{ + Path: "/get", + Headers: requestHeaders, + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + expectOkReq := http.MakeRequest(t, &expectOkResp, gwAddr, "HTTP", "http") + + // send exactly 4 requests, and still expect 200 + + // keep sending requests till get 200 first, that will cost one 200 + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectOkResp) + + // fire the rest of the requests + if err := GotExactExpectedResponse(t, 3, suite.RoundTripper, expectOkReq, expectOkResp); err != nil { + t.Errorf("failed to get expected responses for the request: %v", err) + } + }) + }, +} + var RateLimitHeadersDisabled = suite.ConformanceTest{ ShortName: "RateLimitHeadersDisabled", Description: "Disable rate limit headers", From 8c748d385cd185e40d7c39e00135476a1f0c9bd2 Mon Sep 17 00:00:00 2001 From: zirain Date: Sat, 19 Oct 2024 13:24:24 +0800 Subject: [PATCH 33/56] chore: update logo (#4469) * chore: update logo Signed-off-by: zirain * update Signed-off-by: zirain --------- Signed-off-by: zirain --- site/assets/icons/logo.svg | 219 +++++++++---------------------------- 1 file changed, 51 insertions(+), 168 deletions(-) diff --git a/site/assets/icons/logo.svg b/site/assets/icons/logo.svg index b0e579bd9d4..77ac7ed5386 100644 --- a/site/assets/icons/logo.svg +++ b/site/assets/icons/logo.svg @@ -1,170 +1,53 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From d996c29dc559e3f6c412a3f120834e1a2daee753 Mon Sep 17 00:00:00 2001 From: zirain Date: Sat, 19 Oct 2024 13:25:26 +0800 Subject: [PATCH 34/56] docs: correct the curl command (#4467) Signed-off-by: zirain --- .../content/en/docs/tasks/extensibility/envoy-patch-policy.md | 4 ++-- .../en/latest/tasks/extensibility/envoy-patch-policy.md | 4 ++-- .../content/en/v1.1/tasks/extensibility/envoy-patch-policy.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/site/content/en/docs/tasks/extensibility/envoy-patch-policy.md b/site/content/en/docs/tasks/extensibility/envoy-patch-policy.md index 7fe84762189..3c4c0a4d068 100644 --- a/site/content/en/docs/tasks/extensibility/envoy-patch-policy.md +++ b/site/content/en/docs/tasks/extensibility/envoy-patch-policy.md @@ -269,8 +269,8 @@ kubectl patch httproute backend --type=json --patch ' * Test it out by specifying a path apart from `/get` -``` -$ curl --header "Host: www.example.com" http://localhost:8888/find +```shell +$ curl --header "Host: www.example.com" http://$GATEWAY_HOST/find Handling connection for 8888 could not find what you are looking for ``` diff --git a/site/content/en/latest/tasks/extensibility/envoy-patch-policy.md b/site/content/en/latest/tasks/extensibility/envoy-patch-policy.md index e503244c503..e9709cc7651 100644 --- a/site/content/en/latest/tasks/extensibility/envoy-patch-policy.md +++ b/site/content/en/latest/tasks/extensibility/envoy-patch-policy.md @@ -268,8 +268,8 @@ kubectl patch httproute backend --type=json --patch ' * Test it out by specifying a path apart from `/get` -``` -$ curl --header "Host: www.example.com" http://localhost:8888/find +```shell +$ curl --header "Host: www.example.com" http://$GATEWAY_HOST/find Handling connection for 8888 could not find what you are looking for ``` diff --git a/site/content/en/v1.1/tasks/extensibility/envoy-patch-policy.md b/site/content/en/v1.1/tasks/extensibility/envoy-patch-policy.md index 7fe84762189..3c4c0a4d068 100644 --- a/site/content/en/v1.1/tasks/extensibility/envoy-patch-policy.md +++ b/site/content/en/v1.1/tasks/extensibility/envoy-patch-policy.md @@ -269,8 +269,8 @@ kubectl patch httproute backend --type=json --patch ' * Test it out by specifying a path apart from `/get` -``` -$ curl --header "Host: www.example.com" http://localhost:8888/find +```shell +$ curl --header "Host: www.example.com" http://$GATEWAY_HOST/find Handling connection for 8888 could not find what you are looking for ``` From 3e39606f3fc74a8f2e6d8fb5091d2289bd918a5a Mon Sep 17 00:00:00 2001 From: zirain Date: Sat, 19 Oct 2024 13:28:39 +0800 Subject: [PATCH 35/56] chore: use ptr.Deref to simply code (#4477) Signed-off-by: zirain --- internal/gatewayapi/listener.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/internal/gatewayapi/listener.go b/internal/gatewayapi/listener.go index 5c85e561ea1..51fd1f74da2 100644 --- a/internal/gatewayapi/listener.go +++ b/internal/gatewayapi/listener.go @@ -344,7 +344,8 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * al := &ir.ALSAccessLog{ LogName: logName, Destination: ir.RouteDestination{ - Name: fmt.Sprintf("accesslog_als_%d_%d", i, j), // TODO: rename this, so that we can share backend with tracing? + // TODO: rename this, so that we can share backend with tracing? + Name: fmt.Sprintf("accesslog_als_%d_%d", i, j), Settings: ds, }, Traffic: traffic, @@ -384,7 +385,8 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * CELMatches: validExprs, Resources: sink.OpenTelemetry.Resources, Destination: ir.RouteDestination{ - Name: fmt.Sprintf("accesslog_otel_%d_%d", i, j), // TODO: rename this, so that we can share backend with tracing? + // TODO: rename this, so that we can share backend with tracing? + Name: fmt.Sprintf("accesslog_otel_%d_%d", i, j), Settings: ds, }, Traffic: traffic, @@ -416,7 +418,9 @@ func (t *Translator) processAccessLog(envoyproxy *egv1a1.EnvoyProxy, resources * return irAccessLog, nil } -func (t *Translator) processTracing(gw *gwapiv1.Gateway, envoyproxy *egv1a1.EnvoyProxy, mergeGateways bool, resources *resource.Resources) (*ir.Tracing, error) { +func (t *Translator) processTracing(gw *gwapiv1.Gateway, envoyproxy *egv1a1.EnvoyProxy, + mergeGateways bool, resources *resource.Resources, +) (*ir.Tracing, error) { if envoyproxy == nil || envoyproxy.Spec.Telemetry == nil || envoyproxy.Spec.Telemetry.Tracing == nil { @@ -460,7 +464,8 @@ func (t *Translator) processTracing(gw *gwapiv1.Gateway, envoyproxy *egv1a1.Envo SamplingRate: samplingRate, CustomTags: tracing.CustomTags, Destination: ir.RouteDestination{ - Name: "tracing", // TODO: rename this, so that we can share backend with accesslog? + // TODO: rename this, so that we can share backend with accesslog? + Name: "tracing", Settings: ds, }, Provider: tracing.Provider, @@ -487,13 +492,15 @@ func (t *Translator) processMetrics(envoyproxy *egv1a1.EnvoyProxy, resources *re } return &ir.Metrics{ - EnableVirtualHostStats: envoyproxy.Spec.Telemetry.Metrics.EnableVirtualHostStats != nil && *envoyproxy.Spec.Telemetry.Metrics.EnableVirtualHostStats, - EnablePerEndpointStats: envoyproxy.Spec.Telemetry.Metrics.EnablePerEndpointStats != nil && *envoyproxy.Spec.Telemetry.Metrics.EnablePerEndpointStats, - EnableRequestResponseSizesStats: envoyproxy.Spec.Telemetry.Metrics.EnableRequestResponseSizesStats != nil && *envoyproxy.Spec.Telemetry.Metrics.EnableRequestResponseSizesStats, + EnableVirtualHostStats: ptr.Deref(envoyproxy.Spec.Telemetry.Metrics.EnableVirtualHostStats, false), + EnablePerEndpointStats: ptr.Deref(envoyproxy.Spec.Telemetry.Metrics.EnablePerEndpointStats, false), + EnableRequestResponseSizesStats: ptr.Deref(envoyproxy.Spec.Telemetry.Metrics.EnableRequestResponseSizesStats, false), }, nil } -func (t *Translator) processBackendRefs(backendCluster egv1a1.BackendCluster, namespace string, resources *resource.Resources, envoyProxy *egv1a1.EnvoyProxy) ([]*ir.DestinationSetting, *ir.TrafficFeatures, error) { +func (t *Translator) processBackendRefs(backendCluster egv1a1.BackendCluster, namespace string, + resources *resource.Resources, envoyProxy *egv1a1.EnvoyProxy, +) ([]*ir.DestinationSetting, *ir.TrafficFeatures, error) { traffic, err := translateTrafficFeatures(backendCluster.BackendSettings) if err != nil { return nil, nil, err From 8854d2db3e454e9e908f469f274c83b0f7923248 Mon Sep 17 00:00:00 2001 From: zirain Date: Sat, 19 Oct 2024 13:32:14 +0800 Subject: [PATCH 36/56] chore: bump crd-ref-docs (#4474) * bump crd-ref-docs Signed-off-by: zirain * use go 1.23.1 Signed-off-by: zirain * lint Signed-off-by: zirain --------- Signed-off-by: zirain --- api/v1alpha1/basic_auth_types.go | 6 ++++-- api/v1alpha1/oidc_types.go | 4 ++-- api/v1alpha1/wasm_types.go | 4 ++-- internal/gatewayapi/validate.go | 6 +++--- internal/provider/kubernetes/controller.go | 6 +++--- internal/provider/kubernetes/indexers.go | 2 +- internal/provider/kubernetes/predicates_test.go | 7 +++---- test/e2e/tests/wasm_oci.go | 3 +-- tools/src/crd-ref-docs/go.mod | 4 ++-- tools/src/crd-ref-docs/go.sum | 6 ++---- 10 files changed, 23 insertions(+), 25 deletions(-) diff --git a/api/v1alpha1/basic_auth_types.go b/api/v1alpha1/basic_auth_types.go index 97fa66d5e76..f7bec283780 100644 --- a/api/v1alpha1/basic_auth_types.go +++ b/api/v1alpha1/basic_auth_types.go @@ -5,7 +5,9 @@ package v1alpha1 -import gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" +import ( + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" +) const BasicAuthUsersSecretKey = ".htpasswd" @@ -23,5 +25,5 @@ type BasicAuth struct { // for more details. // // Note: The secret must be in the same namespace as the SecurityPolicy. - Users gwapiv1b1.SecretObjectReference `json:"users"` + Users gwapiv1.SecretObjectReference `json:"users"` } diff --git a/api/v1alpha1/oidc_types.go b/api/v1alpha1/oidc_types.go index 78c32287cde..dcc03615772 100644 --- a/api/v1alpha1/oidc_types.go +++ b/api/v1alpha1/oidc_types.go @@ -7,7 +7,7 @@ package v1alpha1 import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) const OIDCClientSecretKey = "client-secret" @@ -29,7 +29,7 @@ type OIDC struct { // This is an Opaque secret. The client secret should be stored in the key // "client-secret". // +kubebuilder:validation:Required - ClientSecret gwapiv1b1.SecretObjectReference `json:"clientSecret"` + ClientSecret gwapiv1.SecretObjectReference `json:"clientSecret"` // The optional cookie name overrides to be used for Bearer and IdToken cookies in the // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). diff --git a/api/v1alpha1/wasm_types.go b/api/v1alpha1/wasm_types.go index 1c41513f941..66c0e1fc84f 100644 --- a/api/v1alpha1/wasm_types.go +++ b/api/v1alpha1/wasm_types.go @@ -7,7 +7,7 @@ package v1alpha1 import ( apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" ) // Wasm defines a Wasm extension. @@ -136,7 +136,7 @@ type ImageWasmCodeSource struct { // Only support Kubernetes Secret resource from the same namespace. // +kubebuilder:validation:XValidation:message="only support Secret kind.",rule="self.kind == 'Secret'" // +optional - PullSecretRef *gwapiv1b1.SecretObjectReference `json:"pullSecretRef,omitempty"` + PullSecretRef *gwapiv1.SecretObjectReference `json:"pullSecretRef,omitempty"` } // ImagePullPolicy defines the policy to use when pulling an OIC image. diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index 87020d7f6ec..6b9a488b86b 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -914,7 +914,7 @@ func (t *Translator) validateHostname(hostname string) error { func (t *Translator) validateSecretRef( allowCrossNamespace bool, from crossNamespaceFrom, - secretObjRef gwapiv1b1.SecretObjectReference, + secretObjRef gwapiv1.SecretObjectReference, resources *resource.Resources, ) (*corev1.Secret, error) { if err := t.validateSecretObjectRef(allowCrossNamespace, from, secretObjRef, resources); err != nil { @@ -938,7 +938,7 @@ func (t *Translator) validateSecretRef( func (t *Translator) validateConfigMapRef( allowCrossNamespace bool, from crossNamespaceFrom, - secretObjRef gwapiv1b1.SecretObjectReference, + secretObjRef gwapiv1.SecretObjectReference, resources *resource.Resources, ) (*corev1.ConfigMap, error) { if err := t.validateSecretObjectRef(allowCrossNamespace, from, secretObjRef, resources); err != nil { @@ -962,7 +962,7 @@ func (t *Translator) validateConfigMapRef( func (t *Translator) validateSecretObjectRef( allowCrossNamespace bool, from crossNamespaceFrom, - secretRef gwapiv1b1.SecretObjectReference, + secretRef gwapiv1.SecretObjectReference, resources *resource.Resources, ) error { var kind string diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 167e70746ec..7fe3c3d32ff 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -602,7 +602,7 @@ func (r *gatewayAPIReconciler) processSecretRef( ownerKind string, ownerNS string, ownerName string, - secretRef gwapiv1b1.SecretObjectReference, + secretRef gwapiv1.SecretObjectReference, ) error { secret := new(corev1.Secret) secretNS := gatewayapi.NamespaceDerefOr(secretRef.Namespace, ownerNS) @@ -704,7 +704,7 @@ func (r *gatewayAPIReconciler) processConfigMapRef( ownerKind string, ownerNS string, ownerName string, - configMapRef gwapiv1b1.SecretObjectReference, + configMapRef gwapiv1.SecretObjectReference, ) error { configMap := new(corev1.ConfigMap) configMapNS := gatewayapi.NamespaceDerefOr(configMapRef.Namespace, ownerNS) @@ -1794,7 +1794,7 @@ func (r *gatewayAPIReconciler) processBackendTLSPolicyRefs( string(caCertRef.Kind) == resource.KindSecret { var err error - caRefNew := gwapiv1b1.SecretObjectReference{ + caRefNew := gwapiv1.SecretObjectReference{ Group: gatewayapi.GroupPtr(string(caCertRef.Group)), Kind: gatewayapi.KindPtr(string(caCertRef.Kind)), Name: caCertRef.Name, diff --git a/internal/provider/kubernetes/indexers.go b/internal/provider/kubernetes/indexers.go index 68a58dd872d..462a70542f3 100644 --- a/internal/provider/kubernetes/indexers.go +++ b/internal/provider/kubernetes/indexers.go @@ -531,7 +531,7 @@ func secretSecurityPolicyIndexFunc(rawObj client.Object) []string { securityPolicy := rawObj.(*egv1a1.SecurityPolicy) var ( - secretReferences []gwapiv1b1.SecretObjectReference + secretReferences []gwapiv1.SecretObjectReference values []string ) diff --git a/internal/provider/kubernetes/predicates_test.go b/internal/provider/kubernetes/predicates_test.go index ef8182ffdb9..5954e94675e 100644 --- a/internal/provider/kubernetes/predicates_test.go +++ b/internal/provider/kubernetes/predicates_test.go @@ -19,7 +19,6 @@ import ( fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" @@ -261,7 +260,7 @@ func TestValidateSecretForReconcile(t *testing.T) { TokenEndpoint: ptr.To("https://oauth2.googleapis.com/token"), }, ClientID: "client-id", - ClientSecret: gwapiv1b1.SecretObjectReference{ + ClientSecret: gwapiv1.SecretObjectReference{ Name: "secret", }, }, @@ -290,7 +289,7 @@ func TestValidateSecretForReconcile(t *testing.T) { }, }, BasicAuth: &egv1a1.BasicAuth{ - Users: gwapiv1b1.SecretObjectReference{ + Users: gwapiv1.SecretObjectReference{ Name: "secret", }, }, @@ -336,7 +335,7 @@ func TestValidateSecretForReconcile(t *testing.T) { Type: egv1a1.ImageWasmCodeSourceType, Image: &egv1a1.ImageWasmCodeSource{ URL: "https://example.com/testwasm:v1.0.0", - PullSecretRef: &gwapiv1b1.SecretObjectReference{ + PullSecretRef: &gwapiv1.SecretObjectReference{ Name: "secret", }, }, diff --git a/test/e2e/tests/wasm_oci.go b/test/e2e/tests/wasm_oci.go index 1a0a43a33c5..4a6a53f6603 100644 --- a/test/e2e/tests/wasm_oci.go +++ b/test/e2e/tests/wasm_oci.go @@ -32,7 +32,6 @@ import ( "k8s.io/utils/ptr" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gwapiv1b1 "sigs.k8s.io/gateway-api/apis/v1beta1" "sigs.k8s.io/gateway-api/conformance/utils/http" "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" "sigs.k8s.io/gateway-api/conformance/utils/suite" @@ -416,7 +415,7 @@ func createEEPForWasmTest( }, } if withPullSecret { - eep.Spec.Wasm[0].Code.Image.PullSecretRef = &gwapiv1b1.SecretObjectReference{ + eep.Spec.Wasm[0].Code.Image.PullSecretRef = &gwapiv1.SecretObjectReference{ Name: gwapiv1.ObjectName(pullSecret), } } diff --git a/tools/src/crd-ref-docs/go.mod b/tools/src/crd-ref-docs/go.mod index 23b3153de03..017b54837b8 100644 --- a/tools/src/crd-ref-docs/go.mod +++ b/tools/src/crd-ref-docs/go.mod @@ -1,8 +1,8 @@ module local -go 1.22.7 +go 1.23.1 -require github.com/elastic/crd-ref-docs v0.0.13-0.20240723135120-56876bccac3a +require github.com/elastic/crd-ref-docs v0.1.0 require ( github.com/Masterminds/goutils v1.1.1 // indirect diff --git a/tools/src/crd-ref-docs/go.sum b/tools/src/crd-ref-docs/go.sum index 2c129e265b6..8bfb30cec1a 100644 --- a/tools/src/crd-ref-docs/go.sum +++ b/tools/src/crd-ref-docs/go.sum @@ -8,10 +8,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/elastic/crd-ref-docs v0.0.13-0.20240413123740-ea9fcaa0230f h1:cE1CF4Bfi+9gvaNz35jOsp3tFJVm/mFr88szZ41FG8Q= -github.com/elastic/crd-ref-docs v0.0.13-0.20240413123740-ea9fcaa0230f/go.mod h1:X83mMBdJt05heJUYiS3T0yJ/JkCuliuhSUNav5Gjo/U= -github.com/elastic/crd-ref-docs v0.0.13-0.20240723135120-56876bccac3a h1:+sHMdth53bKHbct/BqfYIhYXGKhIZJDv2PhS9Gfw8xg= -github.com/elastic/crd-ref-docs v0.0.13-0.20240723135120-56876bccac3a/go.mod h1:X83mMBdJt05heJUYiS3T0yJ/JkCuliuhSUNav5Gjo/U= +github.com/elastic/crd-ref-docs v0.1.0 h1:Cr5kz89QB3Iuuj7dhAfLMApCrChEGAaIBTxGk/xuRKw= +github.com/elastic/crd-ref-docs v0.1.0/go.mod h1:X83mMBdJt05heJUYiS3T0yJ/JkCuliuhSUNav5Gjo/U= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= From 70c568beae352160cebc138576dccd75239da0e4 Mon Sep 17 00:00:00 2001 From: zirain Date: Sat, 19 Oct 2024 13:40:11 +0800 Subject: [PATCH 37/56] docs: improve a little bit visual (#4468) * docs: improve a little bit visual Signed-off-by: zirain * sidebar Signed-off-by: zirain --------- Signed-off-by: zirain --- site/assets/scss/_variables_project.scss | 9 +++++++++ .../en/docs/tasks/extensibility/extension-server.md | 1 + .../en/latest/tasks/extensibility/extension-server.md | 1 + .../en/v1.1/tasks/extensibility/extension-server.md | 1 + 4 files changed, 12 insertions(+) diff --git a/site/assets/scss/_variables_project.scss b/site/assets/scss/_variables_project.scss index 8c3ce90a2cf..e799cef8e90 100644 --- a/site/assets/scss/_variables_project.scss +++ b/site/assets/scss/_variables_project.scss @@ -11,4 +11,13 @@ $dark: #280C53; // better style when pre inside tab pane .td-content .highlight pre{ margin-bottom: 1rem !important; +} + +nav.foldable-nav .with-child, nav.foldable-nav .without-child { + position: relative; + padding-left: 1.0em !important; +} + +nav.foldable-nav .ul-1 .with-child > label:before { + padding-left: 0 !important; } \ No newline at end of file diff --git a/site/content/en/docs/tasks/extensibility/extension-server.md b/site/content/en/docs/tasks/extensibility/extension-server.md index 7d67c23f6da..323ce5642ea 100644 --- a/site/content/en/docs/tasks/extensibility/extension-server.md +++ b/site/content/en/docs/tasks/extensibility/extension-server.md @@ -1,5 +1,6 @@ --- title: "Envoy Gateway Extension Server" +linkTitle: "Extension Server" --- This task explains how to extend Envoy Gateway using an Extension Server. Envoy Gateway diff --git a/site/content/en/latest/tasks/extensibility/extension-server.md b/site/content/en/latest/tasks/extensibility/extension-server.md index 922f0de7c8e..6d16013d410 100644 --- a/site/content/en/latest/tasks/extensibility/extension-server.md +++ b/site/content/en/latest/tasks/extensibility/extension-server.md @@ -1,5 +1,6 @@ --- title: "Envoy Gateway Extension Server" +linkTitle: "Extension Server" --- This task explains how to extend Envoy Gateway using an Extension Server. Envoy Gateway diff --git a/site/content/en/v1.1/tasks/extensibility/extension-server.md b/site/content/en/v1.1/tasks/extensibility/extension-server.md index 7d67c23f6da..323ce5642ea 100644 --- a/site/content/en/v1.1/tasks/extensibility/extension-server.md +++ b/site/content/en/v1.1/tasks/extensibility/extension-server.md @@ -1,5 +1,6 @@ --- title: "Envoy Gateway Extension Server" +linkTitle: "Extension Server" --- This task explains how to extend Envoy Gateway using an Extension Server. Envoy Gateway From 26ec49ab8ee3a03c90cd705cc9e90f8441cd97f2 Mon Sep 17 00:00:00 2001 From: zirain Date: Mon, 21 Oct 2024 10:59:18 +0800 Subject: [PATCH 38/56] docs: update configuration kick in tips (#4479) * docs: update configuration kick in tips Signed-off-by: zirain * lint Signed-off-by: zirain * update Signed-off-by: zirain --------- Signed-off-by: zirain --- .../en/latest/boilerplates/rollout-envoy-gateway.md | 10 ++++++++++ .../latest/tasks/extensibility/envoy-patch-policy.md | 6 +----- .../tasks/observability/gateway-observability.md | 12 ++---------- .../tasks/observability/rate-limit-observability.md | 6 +----- .../en/latest/tasks/security/private-key-provider.md | 6 +----- site/content/en/latest/tasks/traffic/backend.md | 6 +----- .../en/latest/tasks/traffic/global-rate-limit.md | 12 ++---------- site/layouts/shortcodes/boilerplate.html | 2 +- tools/make/docs.mk | 3 +++ 9 files changed, 22 insertions(+), 41 deletions(-) create mode 100644 site/content/en/latest/boilerplates/rollout-envoy-gateway.md diff --git a/site/content/en/latest/boilerplates/rollout-envoy-gateway.md b/site/content/en/latest/boilerplates/rollout-envoy-gateway.md new file mode 100644 index 00000000000..9072526868c --- /dev/null +++ b/site/content/en/latest/boilerplates/rollout-envoy-gateway.md @@ -0,0 +1,10 @@ +--- +--- + +> After updating the `ConfigMap`, you will need to wait the configuration kicks in.
+> You can **force** the configuration to be reloaded by restarting the `envoy-gateway` deployment. +> +> ```shell +> kubectl rollout restart deployment envoy-gateway -n envoy-gateway-system +> ``` +> \ No newline at end of file diff --git a/site/content/en/latest/tasks/extensibility/envoy-patch-policy.md b/site/content/en/latest/tasks/extensibility/envoy-patch-policy.md index e9709cc7651..54e69f41d0f 100644 --- a/site/content/en/latest/tasks/extensibility/envoy-patch-policy.md +++ b/site/content/en/latest/tasks/extensibility/envoy-patch-policy.md @@ -80,11 +80,7 @@ data: {{% /tab %}} {{< /tabpane >}} -* After updating the `ConfigMap`, you will need to restart the `envoy-gateway` deployment so the configuration kicks in - -```shell -kubectl rollout restart deployment envoy-gateway -n envoy-gateway-system -``` +{{< boilerplate rollout-envoy-gateway >}} ## Testing diff --git a/site/content/en/latest/tasks/observability/gateway-observability.md b/site/content/en/latest/tasks/observability/gateway-observability.md index 6e0040b4f5d..f23eb9097cf 100644 --- a/site/content/en/latest/tasks/observability/gateway-observability.md +++ b/site/content/en/latest/tasks/observability/gateway-observability.md @@ -86,11 +86,7 @@ data: {{% /tab %}} {{< /tabpane >}} -After updating the `ConfigMap`, you will need to restart the `envoy-gateway` deployment so the configuration kicks in: - -```shell -kubectl rollout restart deployment envoy-gateway -n envoy-gateway-system -``` +{{< boilerplate rollout-envoy-gateway >}} ### Enable Open Telemetry sink in Envoy Gateway @@ -157,11 +153,7 @@ data: {{% /tab %}} {{< /tabpane >}} -After updating the `ConfigMap`, you will need to restart the `envoy-gateway` deployment so the configuration kicks in: - -```shell -kubectl rollout restart deployment envoy-gateway -n envoy-gateway-system -``` +{{< boilerplate rollout-envoy-gateway >}} Verify OTel-Collector metrics: diff --git a/site/content/en/latest/tasks/observability/rate-limit-observability.md b/site/content/en/latest/tasks/observability/rate-limit-observability.md index a0e523d6c8a..ec1244f731e 100644 --- a/site/content/en/latest/tasks/observability/rate-limit-observability.md +++ b/site/content/en/latest/tasks/observability/rate-limit-observability.md @@ -91,8 +91,4 @@ data: {{% /tab %}} {{< /tabpane >}} -After updating the ConfigMap, you will need to restart the envoy-gateway deployment so the configuration kicks in: - -```shell -kubectl rollout restart deployment envoy-gateway -n envoy-gateway-system -``` +{{< boilerplate rollout-envoy-gateway >}} diff --git a/site/content/en/latest/tasks/security/private-key-provider.md b/site/content/en/latest/tasks/security/private-key-provider.md index 529056b33e1..24544f67973 100644 --- a/site/content/en/latest/tasks/security/private-key-provider.md +++ b/site/content/en/latest/tasks/security/private-key-provider.md @@ -169,11 +169,7 @@ data: {{% /tab %}} {{< /tabpane >}} -* After updating the `ConfigMap`, you will need to restart the `envoy-gateway` deployment so the configuration kicks in - - ```shell - kubectl rollout restart deployment envoy-gateway -n envoy-gateway-system - ``` +{{< boilerplate rollout-envoy-gateway >}} ## Create gateway for TLS termination diff --git a/site/content/en/latest/tasks/traffic/backend.md b/site/content/en/latest/tasks/traffic/backend.md index 0f2ade4dadd..2bb2a4e647a 100644 --- a/site/content/en/latest/tasks/traffic/backend.md +++ b/site/content/en/latest/tasks/traffic/backend.md @@ -94,11 +94,7 @@ data: {{% /tab %}} {{< /tabpane >}} -* After updating the `ConfigMap`, you will need to restart the `envoy-gateway` deployment so the configuration kicks in - -```shell -kubectl rollout restart deployment envoy-gateway -n envoy-gateway-system -``` +{{< boilerplate rollout-envoy-gateway >}} ## Testing diff --git a/site/content/en/latest/tasks/traffic/global-rate-limit.md b/site/content/en/latest/tasks/traffic/global-rate-limit.md index 6c96b12efe7..15cc462dbf4 100644 --- a/site/content/en/latest/tasks/traffic/global-rate-limit.md +++ b/site/content/en/latest/tasks/traffic/global-rate-limit.md @@ -214,11 +214,7 @@ data: {{% /tab %}} {{< /tabpane >}} -* After updating the `ConfigMap`, you will need to restart the `envoy-gateway` deployment so the configuration kicks in - -```shell -kubectl rollout restart deployment envoy-gateway -n envoy-gateway-system -``` +{{< boilerplate rollout-envoy-gateway >}} ## Rate Limit Specific User @@ -1287,11 +1283,7 @@ data: {{% /tab %}} {{< /tabpane >}} -* After updating the `ConfigMap`, you will need to restart the `envoy-gateway` deployment so the configuration kicks in - -```shell -kubectl rollout restart deployment envoy-gateway -n envoy-gateway-system -``` +{{< boilerplate rollout-envoy-gateway >}} [Global Rate Limiting]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/global_rate_limiting [Local rate limiting]: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/other_features/local_rate_limiting diff --git a/site/layouts/shortcodes/boilerplate.html b/site/layouts/shortcodes/boilerplate.html index b120a8d1b14..752c4ad2e89 100644 --- a/site/layouts/shortcodes/boilerplate.html +++ b/site/layouts/shortcodes/boilerplate.html @@ -15,7 +15,7 @@ {{- $pattern := printf "%s*" $name -}} {{- $resource := $bundle.Resources.GetMatch $pattern -}} {{- with $resource -}} - {{- .Content | markdownify -}} + {{- .Content -}} {{- else -}} {{- errorf "Could not find boilerplate '%s' (%s)" $name $position -}} {{- end -}} diff --git a/tools/make/docs.mk b/tools/make/docs.mk index d6c6b4d8232..698896f089a 100644 --- a/tools/make/docs.mk +++ b/tools/make/docs.mk @@ -118,6 +118,9 @@ docs-check-links: # Check for broken links in the docs @$(LOG_TARGET) linkinator site/public/ -r --concurrency 25 --skip $(LINKINATOR_IGNORE) +docs-markdown-lint: + markdownlint -c .github/markdown_lint_config.json site/content/* + release-notes-docs: $(tools/release-notes-docs) @$(LOG_TARGET) @for file in $(wildcard release-notes/*.yaml); do \ From 319283e4ca5d9d27ddb4b5fc035b4490a03a5a9b Mon Sep 17 00:00:00 2001 From: Huabing Zhao Date: Tue, 22 Oct 2024 01:23:24 +0800 Subject: [PATCH 39/56] Fix wasm example link (#4491) fix wasm example link Signed-off-by: Huabing Zhao --- site/content/en/docs/tasks/extensibility/wasm.md | 6 +++--- site/content/en/latest/tasks/extensibility/wasm.md | 6 +++--- site/content/en/v1.1/tasks/extensibility/wasm.md | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/site/content/en/docs/tasks/extensibility/wasm.md b/site/content/en/docs/tasks/extensibility/wasm.md index cb2e013dd80..1b1d32f9ecb 100644 --- a/site/content/en/docs/tasks/extensibility/wasm.md +++ b/site/content/en/docs/tasks/extensibility/wasm.md @@ -23,7 +23,7 @@ kubectl get gateway/eg -o yaml ## Configuration -Envoy Gateway supports two types of Wasm extensions: +Envoy Gateway supports two types of Wasm extensions: * HTTP Wasm Extension: The Wasm extension is fetched from a remote URL. * Image Wasm Extension: The Wasm extension is packaged as an OCI image and fetched from an image registry. @@ -54,7 +54,7 @@ spec: code: type: HTTP http: - url: https://raw.githubusercontent.com/envoyproxy/envoy/main/examples/wasm-cc/lib/envoy_filter_http_wasm_example.wasm + url: https://raw.githubusercontent.com/envoyproxy/examples/main/wasm-cc/lib/envoy_filter_http_wasm_example.wasm sha256: 79c9f85128bb0177b6511afa85d587224efded376ac0ef76df56595f1e6315c0 EOF ``` @@ -80,7 +80,7 @@ spec: code: type: HTTP http: - url: https://raw.githubusercontent.com/envoyproxy/envoy/main/examples/wasm-cc/lib/envoy_filter_http_wasm_example.wasm + url: https://raw.githubusercontent.com/envoyproxy/examples/main/wasm-cc/lib/envoy_filter_http_wasm_example.wasm sha256: 79c9f85128bb0177b6511afa85d587224efded376ac0ef76df56595f1e6315c0 ``` diff --git a/site/content/en/latest/tasks/extensibility/wasm.md b/site/content/en/latest/tasks/extensibility/wasm.md index 8a640471ee1..baad6a5804f 100644 --- a/site/content/en/latest/tasks/extensibility/wasm.md +++ b/site/content/en/latest/tasks/extensibility/wasm.md @@ -16,7 +16,7 @@ This instantiated resource can be linked to a [Gateway][Gateway] and [HTTPRoute] ## Configuration -Envoy Gateway supports two types of Wasm extensions: +Envoy Gateway supports two types of Wasm extensions: * HTTP Wasm Extension: The Wasm extension is fetched from a remote URL. * Image Wasm Extension: The Wasm extension is packaged as an OCI image and fetched from an image registry. @@ -47,7 +47,7 @@ spec: code: type: HTTP http: - url: https://raw.githubusercontent.com/envoyproxy/envoy/main/examples/wasm-cc/lib/envoy_filter_http_wasm_example.wasm + url: https://raw.githubusercontent.com/envoyproxy/examples/main/wasm-cc/lib/envoy_filter_http_wasm_example.wasm sha256: 79c9f85128bb0177b6511afa85d587224efded376ac0ef76df56595f1e6315c0 EOF ``` @@ -73,7 +73,7 @@ spec: code: type: HTTP http: - url: https://raw.githubusercontent.com/envoyproxy/envoy/main/examples/wasm-cc/lib/envoy_filter_http_wasm_example.wasm + url: https://raw.githubusercontent.com/envoyproxy/examples/main/wasm-cc/lib/envoy_filter_http_wasm_example.wasm sha256: 79c9f85128bb0177b6511afa85d587224efded376ac0ef76df56595f1e6315c0 ``` diff --git a/site/content/en/v1.1/tasks/extensibility/wasm.md b/site/content/en/v1.1/tasks/extensibility/wasm.md index cb2e013dd80..1b1d32f9ecb 100644 --- a/site/content/en/v1.1/tasks/extensibility/wasm.md +++ b/site/content/en/v1.1/tasks/extensibility/wasm.md @@ -23,7 +23,7 @@ kubectl get gateway/eg -o yaml ## Configuration -Envoy Gateway supports two types of Wasm extensions: +Envoy Gateway supports two types of Wasm extensions: * HTTP Wasm Extension: The Wasm extension is fetched from a remote URL. * Image Wasm Extension: The Wasm extension is packaged as an OCI image and fetched from an image registry. @@ -54,7 +54,7 @@ spec: code: type: HTTP http: - url: https://raw.githubusercontent.com/envoyproxy/envoy/main/examples/wasm-cc/lib/envoy_filter_http_wasm_example.wasm + url: https://raw.githubusercontent.com/envoyproxy/examples/main/wasm-cc/lib/envoy_filter_http_wasm_example.wasm sha256: 79c9f85128bb0177b6511afa85d587224efded376ac0ef76df56595f1e6315c0 EOF ``` @@ -80,7 +80,7 @@ spec: code: type: HTTP http: - url: https://raw.githubusercontent.com/envoyproxy/envoy/main/examples/wasm-cc/lib/envoy_filter_http_wasm_example.wasm + url: https://raw.githubusercontent.com/envoyproxy/examples/main/wasm-cc/lib/envoy_filter_http_wasm_example.wasm sha256: 79c9f85128bb0177b6511afa85d587224efded376ac0ef76df56595f1e6315c0 ``` From 7ad22df2817b126c95eb1d36a732da872519468e Mon Sep 17 00:00:00 2001 From: Huabing Zhao Date: Tue, 22 Oct 2024 01:30:41 +0800 Subject: [PATCH 40/56] Unhide BackendCluster for OIDC provider (#4490) unhide BackendCluster for OIDC provider Signed-off-by: Huabing Zhao --- api/v1alpha1/oidc_types.go | 1 - 1 file changed, 1 deletion(-) diff --git a/api/v1alpha1/oidc_types.go b/api/v1alpha1/oidc_types.go index dcc03615772..dfe7a4604f4 100644 --- a/api/v1alpha1/oidc_types.go +++ b/api/v1alpha1/oidc_types.go @@ -119,7 +119,6 @@ type OIDCProvider struct { // Other settings for the connection to the OIDC Provider can be specified in the BackendSettings resource. // // +optional - // +notImplementedHide BackendCluster `json:",inline"` // The OIDC Provider's [issuer identifier](https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery). From 996d753538855880bfb75db019de32a9843ec26f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:48:51 -0700 Subject: [PATCH 41/56] build(deps): bump aquasecurity/trivy-action from 0.27.0 to 0.28.0 (#4484) Bumps [aquasecurity/trivy-action](https://github.com/aquasecurity/trivy-action) from 0.27.0 to 0.28.0. - [Release notes](https://github.com/aquasecurity/trivy-action/releases) - [Commits](https://github.com/aquasecurity/trivy-action/compare/5681af892cd0f4997658e2bacc62bd0a894cf564...915b19bbe73b92a6cf82a1bc12b087c9a19a5fe2) --- updated-dependencies: - dependency-name: aquasecurity/trivy-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/trivy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 9e4a1460f8e..f34bd237a88 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -25,7 +25,7 @@ jobs: IMAGE=envoy-proxy/gateway-dev TAG=${{ github.sha }} make image - name: Run Trivy vulnerability scanner - uses: aquasecurity/trivy-action@5681af892cd0f4997658e2bacc62bd0a894cf564 # v0.27.0 + uses: aquasecurity/trivy-action@915b19bbe73b92a6cf82a1bc12b087c9a19a5fe2 # v0.28.0 with: image-ref: envoy-proxy/gateway-dev:${{ github.sha }} exit-code: '1' From faa615fc4a5e455429b2e0202caa38d7369a1742 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:49:53 -0700 Subject: [PATCH 42/56] build(deps): bump github.com/prometheus/client_golang from 1.20.4 to 1.20.5 (#4486) build(deps): bump github.com/prometheus/client_golang Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.20.4 to 1.20.5. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.20.4...v1.20.5) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0b7c29eb542..4090b698e5e 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 github.com/miekg/dns v1.1.62 github.com/ohler55/ojg v1.24.1 - github.com/prometheus/client_golang v1.20.4 + github.com/prometheus/client_golang v1.20.5 github.com/prometheus/common v0.60.0 github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index 5aa804824a4..dcf087e087a 100644 --- a/go.sum +++ b/go.sum @@ -689,8 +689,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= -github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= From 138abd12a69826159ce7bf7e55ee09390b7d8148 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:50:47 -0700 Subject: [PATCH 43/56] build(deps): bump github.com/urfave/cli/v2 from 2.27.4 to 2.27.5 in /examples/extension-server (#4488) build(deps): bump github.com/urfave/cli/v2 in /examples/extension-server Bumps [github.com/urfave/cli/v2](https://github.com/urfave/cli) from 2.27.4 to 2.27.5. - [Release notes](https://github.com/urfave/cli/releases) - [Changelog](https://github.com/urfave/cli/blob/main/docs/CHANGELOG.md) - [Commits](https://github.com/urfave/cli/compare/v2.27.4...v2.27.5) --- updated-dependencies: - dependency-name: github.com/urfave/cli/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- examples/extension-server/go.mod | 4 ++-- examples/extension-server/go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/extension-server/go.mod b/examples/extension-server/go.mod index 92af0438105..25eb15516ef 100644 --- a/examples/extension-server/go.mod +++ b/examples/extension-server/go.mod @@ -5,7 +5,7 @@ go 1.23.1 require ( github.com/envoyproxy/gateway v1.0.2 github.com/envoyproxy/go-control-plane v0.13.1 - github.com/urfave/cli/v2 v2.27.4 + github.com/urfave/cli/v2 v2.27.5 google.golang.org/grpc v1.67.1 google.golang.org/protobuf v1.35.1 k8s.io/apimachinery v0.31.1 @@ -17,7 +17,7 @@ require ( cel.dev/expr v0.16.0 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect diff --git a/examples/extension-server/go.sum b/examples/extension-server/go.sum index 1df719e00af..29bfba9e9f4 100644 --- a/examples/extension-server/go.sum +++ b/examples/extension-server/go.sum @@ -4,8 +4,8 @@ github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMr github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20 h1:N+3sFI5GUjRKBi+i0TxYVST9h4Ie192jJWpHvthBBgg= github.com/cncf/xds/go v0.0.0-20240723142845-024c85f92f20/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -64,8 +64,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= -github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= From 83af7d4f8e89f37d6ec125665f09c082c0fe390f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:51:11 -0700 Subject: [PATCH 44/56] build(deps): bump github/codeql-action from 3.26.12 to 3.26.13 (#4485) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.12 to 3.26.13. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/c36620d31ac7c881962c3d9dd939c40ec9434f2b...f779452ac5af1c261dce0346a8f964149f49322b) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 6 +++--- .github/workflows/scorecard.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0f4771a3f5e..5fceea67877 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -36,14 +36,14 @@ jobs: - uses: ./tools/github-actions/setup-deps - name: Initialize CodeQL - uses: github/codeql-action/init@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/init@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/autobuild@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/analyze@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 987f7c2b62d..018bb5c0dd7 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -40,6 +40,6 @@ jobs: retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@c36620d31ac7c881962c3d9dd939c40ec9434f2b # v3.26.12 + uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13 with: sarif_file: results.sarif From 66c0b510a0ff71af0755cd5a24a5cc85d62b61df Mon Sep 17 00:00:00 2001 From: sh2 Date: Tue, 22 Oct 2024 02:07:13 +0800 Subject: [PATCH 45/56] fix: ratelimit not working with both headers and cidr matches (#4377) * fix ratelimit descriptors do not respect both headers and cidr match for one rule Signed-off-by: shawnh2 * fix gen-check and lint Signed-off-by: shawnh2 * fix ratelimit e2e test Signed-off-by: shawnh2 * add more comment and update test case Signed-off-by: shawnh2 --------- Signed-off-by: shawnh2 Co-authored-by: Huabing Zhao --- internal/xds/translator/ratelimit.go | 85 ++++++------- .../header-and-cidr-matches.yaml | 38 ++++++ .../in/xds-ir/ratelimit-headers-and-cidr.yaml | 88 ++++++++++++++ .../header-and-cidr-matches.yaml | 38 ++++++ .../ratelimit-headers-and-cidr.clusters.yaml | 98 +++++++++++++++ .../ratelimit-headers-and-cidr.endpoints.yaml | 36 ++++++ .../ratelimit-headers-and-cidr.listeners.yaml | 44 +++++++ .../ratelimit-headers-and-cidr.routes.yaml | 88 ++++++++++++++ .../ratelimit-headers-and-cidr-match.yaml | 45 +++++++ test/e2e/tests/ratelimit.go | 113 ++++++++++++++++++ 10 files changed, 633 insertions(+), 40 deletions(-) create mode 100644 internal/xds/translator/testdata/in/ratelimit-config/header-and-cidr-matches.yaml create mode 100644 internal/xds/translator/testdata/in/xds-ir/ratelimit-headers-and-cidr.yaml create mode 100644 internal/xds/translator/testdata/out/ratelimit-config/header-and-cidr-matches.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.clusters.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.endpoints.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.listeners.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.routes.yaml create mode 100644 test/e2e/testdata/ratelimit-headers-and-cidr-match.yaml diff --git a/internal/xds/translator/ratelimit.go b/internal/xds/translator/ratelimit.go index 660bc2a7dec..06b37bc4589 100644 --- a/internal/xds/translator/ratelimit.go +++ b/internal/xds/translator/ratelimit.go @@ -157,11 +157,12 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [ // Matches are ANDed rlActions := []*routev3.RateLimit_Action{routeDescriptor} for mIdx, match := range rule.HeaderMatches { + var action *routev3.RateLimit_Action // Case for distinct match if match.Distinct { // Setup RequestHeader actions descriptorKey := getRouteRuleDescriptor(rIdx, mIdx) - action := &routev3.RateLimit_Action{ + action = &routev3.RateLimit_Action{ ActionSpecifier: &routev3.RateLimit_Action_RequestHeaders_{ RequestHeaders: &routev3.RateLimit_Action_RequestHeaders{ HeaderName: match.Name, @@ -169,7 +170,6 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [ }, }, } - rlActions = append(rlActions, action) } else { // Setup HeaderValueMatch actions descriptorKey := getRouteRuleDescriptor(rIdx, mIdx) @@ -184,7 +184,7 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [ if match.Invert != nil && *match.Invert { expectMatch = false } - action := &routev3.RateLimit_Action{ + action = &routev3.RateLimit_Action{ ActionSpecifier: &routev3.RateLimit_Action_HeaderValueMatch_{ HeaderValueMatch: &routev3.RateLimit_Action_HeaderValueMatch{ DescriptorKey: descriptorKey, @@ -196,8 +196,8 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [ }, }, } - rlActions = append(rlActions, action) } + rlActions = append(rlActions, action) } // To be able to rate limit each individual IP, we need to use a nested descriptors structure in the configuration @@ -236,7 +236,7 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [ // Setup RemoteAddress action if distinct match is set if rule.CIDRMatch.Distinct { // Setup RemoteAddress action - action := &routev3.RateLimit_Action{ + action = &routev3.RateLimit_Action{ ActionSpecifier: &routev3.RateLimit_Action_RemoteAddress_{ RemoteAddress: &routev3.RateLimit_Action_RemoteAddress{}, }, @@ -245,8 +245,8 @@ func buildRouteRateLimits(descriptorPrefix string, global *ir.GlobalRateLimit) [ } } - // Case when header match is not set and the rate limit is applied - // to all traffic. + // Case when both header and cidr match are not set and the ratelimit + // will be applied to all traffic. if !rule.IsMatchSet() { // Setup GenericKey action action := &routev3.RateLimit_Action{ @@ -333,22 +333,21 @@ func BuildRateLimitServiceConfig(irListener *ir.HTTPListener) *rlsconfv3.RateLim func buildRateLimitServiceDescriptors(global *ir.GlobalRateLimit) []*rlsconfv3.RateLimitDescriptor { pbDescriptors := make([]*rlsconfv3.RateLimitDescriptor, 0, len(global.Rules)) + // The order in which matching descriptors are built is consistent with + // the order in which ratelimit actions are built: + // 1) Header Matches + // 2) CIDR Match + // 3) No Match for rIdx, rule := range global.Rules { - var head, cur *rlsconfv3.RateLimitDescriptor - if !rule.IsMatchSet() { - pbDesc := new(rlsconfv3.RateLimitDescriptor) - // GenericKey case - pbDesc.Key = getRouteRuleDescriptor(rIdx, -1) - pbDesc.Value = getRouteRuleDescriptor(rIdx, -1) - rateLimit := rlsconfv3.RateLimitPolicy{ - RequestsPerUnit: uint32(rule.Limit.Requests), - Unit: rlsconfv3.RateLimitUnit(rlsconfv3.RateLimitUnit_value[strings.ToUpper(string(rule.Limit.Unit))]), - } - pbDesc.RateLimit = &rateLimit - head = pbDesc - cur = head + rateLimitPolicy := &rlsconfv3.RateLimitPolicy{ + RequestsPerUnit: uint32(rule.Limit.Requests), + Unit: rlsconfv3.RateLimitUnit(rlsconfv3.RateLimitUnit_value[strings.ToUpper(string(rule.Limit.Unit))]), } + // We use a chain structure to describe the matching descriptors for one rule. + // The RateLimitPolicy should be added to the last descriptor in the chain. + var head, cur *rlsconfv3.RateLimitDescriptor + for mIdx, match := range rule.HeaderMatches { pbDesc := new(rlsconfv3.RateLimitDescriptor) // Case for distinct match @@ -361,15 +360,6 @@ func buildRateLimitServiceDescriptors(global *ir.GlobalRateLimit) []*rlsconfv3.R pbDesc.Value = getRouteRuleDescriptor(rIdx, mIdx) } - // Add the ratelimit values to the last descriptor - if mIdx == len(rule.HeaderMatches)-1 { - rateLimit := rlsconfv3.RateLimitPolicy{ - RequestsPerUnit: uint32(rule.Limit.Requests), - Unit: rlsconfv3.RateLimitUnit(rlsconfv3.RateLimitUnit_value[strings.ToUpper(string(rule.Limit.Unit))]), - } - pbDesc.RateLimit = &rateLimit - } - if mIdx == 0 { head = pbDesc } else { @@ -377,6 +367,9 @@ func buildRateLimitServiceDescriptors(global *ir.GlobalRateLimit) []*rlsconfv3.R } cur = pbDesc + + // Do not add the RateLimitPolicy to the last header match descriptor yet, + // as it is also possible that CIDR match descriptor also exist. } // EG supports two kinds of rate limit descriptors for the source IP: exact and distinct. @@ -405,25 +398,37 @@ func buildRateLimitServiceDescriptors(global *ir.GlobalRateLimit) []*rlsconfv3.R pbDesc := new(rlsconfv3.RateLimitDescriptor) pbDesc.Key = "masked_remote_address" pbDesc.Value = rule.CIDRMatch.CIDR - rateLimit := rlsconfv3.RateLimitPolicy{ - RequestsPerUnit: uint32(rule.Limit.Requests), - Unit: rlsconfv3.RateLimitUnit(rlsconfv3.RateLimitUnit_value[strings.ToUpper(string(rule.Limit.Unit))]), + + if cur != nil { + // The header match descriptor chain exist, add current + // descriptor to the chain. + cur.Descriptors = []*rlsconfv3.RateLimitDescriptor{pbDesc} + } else { + head = pbDesc } + cur = pbDesc if rule.CIDRMatch.Distinct { - pbDesc.Descriptors = []*rlsconfv3.RateLimitDescriptor{ - { - Key: "remote_address", - RateLimit: &rateLimit, - }, - } - } else { - pbDesc.RateLimit = &rateLimit + pbDesc := new(rlsconfv3.RateLimitDescriptor) + pbDesc.Key = "remote_address" + cur.Descriptors = []*rlsconfv3.RateLimitDescriptor{pbDesc} + cur = pbDesc } + } + + // Case when both header and cidr match are not set and the ratelimit + // will be applied to all traffic. + if !rule.IsMatchSet() { + pbDesc := new(rlsconfv3.RateLimitDescriptor) + // GenericKey case + pbDesc.Key = getRouteRuleDescriptor(rIdx, -1) + pbDesc.Value = getRouteRuleDescriptor(rIdx, -1) head = pbDesc cur = head } + // Add the ratelimit policy to the last descriptor of chain. + cur.RateLimit = rateLimitPolicy pbDescriptors = append(pbDescriptors, head) } diff --git a/internal/xds/translator/testdata/in/ratelimit-config/header-and-cidr-matches.yaml b/internal/xds/translator/testdata/in/ratelimit-config/header-and-cidr-matches.yaml new file mode 100644 index 00000000000..481b8598695 --- /dev/null +++ b/internal/xds/translator/testdata/in/ratelimit-config/header-and-cidr-matches.yaml @@ -0,0 +1,38 @@ +name: "first-listener" +address: "0.0.0.0" +port: 10080 +hostnames: +- "*" +path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect +routes: +- name: "first-route" + traffic: + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + exact: "one" + - name: "x-user-id" + exact: "two" + - name: "x-org-id" + exact: "three" + cidrMatch: + cidr: 0.0.0.0/0 + ip: 0.0.0.0 + maskLen: 0 + isIPv6: false + distinct: false + limit: + requests: 5 + unit: second + pathMatch: + exact: "foo/bar" + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/in/xds-ir/ratelimit-headers-and-cidr.yaml b/internal/xds/translator/testdata/in/xds-ir/ratelimit-headers-and-cidr.yaml new file mode 100644 index 00000000000..fa9b6f31ae5 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/ratelimit-headers-and-cidr.yaml @@ -0,0 +1,88 @@ +http: +- name: "first-listener" + address: "0.0.0.0" + port: 10080 + hostnames: + - "*" + path: + mergeSlashes: true + escapedSlashesAction: UnescapeAndRedirect + routes: + - name: "first-route" + hostname: "*" + traffic: + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + exact: "one" + cidrMatch: + cidr: 192.168.0.0/16 + maskLen: 16 + limit: + requests: 5 + unit: second + destination: + name: "first-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "second-route" + hostname: "*" + traffic: + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + distinct: true + - name: "foobar" + distinct: true + cidrMatch: + cidr: 192.168.0.0/16 + maskLen: 16 + limit: + requests: 5 + unit: second + pathMatch: + exact: "example" + destination: + name: "second-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 + - name: "third-route" + hostname: "*" + traffic: + rateLimit: + global: + rules: + - headerMatches: + - name: "x-user-id" + exact: "one" + cidrMatch: + cidr: 192.168.0.0/16 + maskLen: 16 + limit: + requests: 5 + unit: second + - headerMatches: + - name: "x-user-id" + exact: "two" + - name: "foobar" + distinct: true + cidrMatch: + cidr: 192.169.0.0/16 + maskLen: 16 + limit: + requests: 10 + unit: second + destination: + name: "third-route-dest" + settings: + - endpoints: + - host: "1.2.3.4" + port: 50000 diff --git a/internal/xds/translator/testdata/out/ratelimit-config/header-and-cidr-matches.yaml b/internal/xds/translator/testdata/out/ratelimit-config/header-and-cidr-matches.yaml new file mode 100644 index 00000000000..83f5376dade --- /dev/null +++ b/internal/xds/translator/testdata/out/ratelimit-config/header-and-cidr-matches.yaml @@ -0,0 +1,38 @@ +name: first-listener +domain: first-listener +descriptors: + - key: first-route + value: first-route + rate_limit: null + descriptors: + - key: rule-0-match-0 + value: rule-0-match-0 + rate_limit: null + descriptors: + - key: rule-0-match-1 + value: rule-0-match-1 + rate_limit: null + descriptors: + - key: rule-0-match-2 + value: rule-0-match-2 + rate_limit: null + descriptors: + - key: masked_remote_address + value: 0.0.0.0/0 + rate_limit: + requests_per_unit: 5 + unit: SECOND + unlimited: false + name: "" + replaces: [] + descriptors: [] + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false + shadow_mode: false + detailed_metric: false diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.clusters.yaml new file mode 100644 index 00000000000..0ba1749076a --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.clusters.yaml @@ -0,0 +1,98 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: first-route-dest + lbPolicy: LEAST_REQUEST + name: first-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: second-route-dest + lbPolicy: LEAST_REQUEST + name: second-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: third-route-dest + lbPolicy: LEAST_REQUEST + name: third-route-dest + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + dnsRefreshRate: 30s + lbPolicy: LEAST_REQUEST + loadAssignment: + clusterName: ratelimit_cluster + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: envoy-ratelimit.envoy-gateway-system.svc.cluster.local + portValue: 8081 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: ratelimit_cluster/backend/0 + name: ratelimit_cluster + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + respectDnsTtl: true + transportSocket: + name: envoy.transport_sockets.tls + typedConfig: + '@type': type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + commonTlsContext: + tlsCertificates: + - certificateChain: + filename: /certs/tls.crt + privateKey: + filename: /certs/tls.key + validationContext: + trustedCa: + filename: /certs/ca.crt + type: STRICT_DNS + typedExtensionProtocolOptions: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + '@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions + explicitHttpConfig: + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.endpoints.yaml new file mode 100644 index 00000000000..475b89a087c --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.endpoints.yaml @@ -0,0 +1,36 @@ +- clusterName: first-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: first-route-dest/backend/0 +- clusterName: second-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: second-route-dest/backend/0 +- clusterName: third-route-dest + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 1.2.3.4 + portValue: 50000 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: third-route-dest/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.listeners.yaml new file mode 100644 index 00000000000..a80f448f017 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.listeners.yaml @@ -0,0 +1,44 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - name: envoy.filters.http.ratelimit + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit + domain: first-listener + enableXRatelimitHeaders: DRAFT_VERSION_03 + rateLimitService: + grpcService: + envoyGrpc: + clusterName: ratelimit_cluster + transportApiVersion: V3 + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: first-listener + serverHeaderTransformation: PASS_THROUGH + statPrefix: http-10080 + useRemoteAddress: true + name: first-listener + name: first-listener + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.routes.yaml new file mode 100644 index 00000000000..459d975a9b0 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/ratelimit-headers-and-cidr.routes.yaml @@ -0,0 +1,88 @@ +- ignorePortInHostMatching: true + name: first-listener + virtualHosts: + - domains: + - '*' + name: first-listener/* + routes: + - match: + prefix: / + name: first-route + route: + cluster: first-route-dest + rateLimits: + - actions: + - genericKey: + descriptorKey: first-route + descriptorValue: first-route + - headerValueMatch: + descriptorKey: rule-0-match-0 + descriptorValue: rule-0-match-0 + expectMatch: true + headers: + - name: x-user-id + stringMatch: + exact: one + - maskedRemoteAddress: + v4PrefixMaskLen: 16 + upgradeConfigs: + - upgradeType: websocket + - match: + path: example + name: second-route + route: + cluster: second-route-dest + rateLimits: + - actions: + - genericKey: + descriptorKey: second-route + descriptorValue: second-route + - requestHeaders: + descriptorKey: rule-0-match-0 + headerName: x-user-id + - requestHeaders: + descriptorKey: rule-0-match-1 + headerName: foobar + - maskedRemoteAddress: + v4PrefixMaskLen: 16 + upgradeConfigs: + - upgradeType: websocket + - match: + prefix: / + name: third-route + route: + cluster: third-route-dest + rateLimits: + - actions: + - genericKey: + descriptorKey: third-route + descriptorValue: third-route + - headerValueMatch: + descriptorKey: rule-0-match-0 + descriptorValue: rule-0-match-0 + expectMatch: true + headers: + - name: x-user-id + stringMatch: + exact: one + - maskedRemoteAddress: + v4PrefixMaskLen: 16 + - actions: + - genericKey: + descriptorKey: third-route + descriptorValue: third-route + - headerValueMatch: + descriptorKey: rule-1-match-0 + descriptorValue: rule-1-match-0 + expectMatch: true + headers: + - name: x-user-id + stringMatch: + exact: two + - requestHeaders: + descriptorKey: rule-1-match-1 + headerName: foobar + - maskedRemoteAddress: + v4PrefixMaskLen: 16 + upgradeConfigs: + - upgradeType: websocket diff --git a/test/e2e/testdata/ratelimit-headers-and-cidr-match.yaml b/test/e2e/testdata/ratelimit-headers-and-cidr-match.yaml new file mode 100644 index 00000000000..fef2f645a2b --- /dev/null +++ b/test/e2e/testdata/ratelimit-headers-and-cidr-match.yaml @@ -0,0 +1,45 @@ +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-headers-and-cidr + namespace: gateway-conformance-infra +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: header-and-cidr-ratelimit + rateLimit: + type: Global + global: + rules: + - clientSelectors: + - headers: + - name: x-user-id + type: Exact + value: one + - name: x-user-org + type: Exact + value: acme + sourceCIDR: + value: 0.0.0.0/0 + type: Distinct + limit: + requests: 3 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: header-and-cidr-ratelimit + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + rules: + - matches: + - path: + type: PathPrefix + value: /get + backendRefs: + - name: infra-backend-v1 + port: 8080 diff --git a/test/e2e/tests/ratelimit.go b/test/e2e/tests/ratelimit.go index b87576b60aa..f0082d77e85 100644 --- a/test/e2e/tests/ratelimit.go +++ b/test/e2e/tests/ratelimit.go @@ -30,6 +30,7 @@ func init() { ConformanceTests = append(ConformanceTests, RateLimitHeadersDisabled) ConformanceTests = append(ConformanceTests, RateLimitBasedJwtClaimsTest) ConformanceTests = append(ConformanceTests, RateLimitMultipleListenersTest) + ConformanceTests = append(ConformanceTests, RateLimitHeadersAndCIDRMatchTest) } var RateLimitCIDRMatchTest = suite.ConformanceTest{ @@ -538,6 +539,118 @@ var RateLimitMultipleListenersTest = suite.ConformanceTest{ }, } +var RateLimitHeadersAndCIDRMatchTest = suite.ConformanceTest{ + ShortName: "RateLimitHeadersAndCIDRMatch", + Description: "Limit requests on rule that has both headers and cidr matches", + Manifests: []string{"testdata/ratelimit-headers-and-cidr-match.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "header-and-cidr-ratelimit", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + t.Run("all matched both headers and cidr can got limited", func(t *testing.T) { + requestHeaders := map[string]string{ + "x-user-id": "one", + "x-user-org": "acme", + } + + ratelimitHeader := make(map[string]string) + expectOkResp := http.ExpectedResponse{ + Request: http.Request{ + Path: "/get", + Headers: requestHeaders, + }, + Response: http.Response{ + StatusCode: 200, + Headers: ratelimitHeader, + }, + Namespace: ns, + } + expectOkResp.Response.Headers["X-Ratelimit-Limit"] = "3, 3;w=3600" + expectOkReq := http.MakeRequest(t, &expectOkResp, gwAddr, "HTTP", "http") + + expectLimitResp := http.ExpectedResponse{ + Request: http.Request{ + Path: "/get", + Headers: requestHeaders, + }, + Response: http.Response{ + StatusCode: 429, + }, + Namespace: ns, + } + expectLimitReq := http.MakeRequest(t, &expectLimitResp, gwAddr, "HTTP", "http") + + // should just send exactly 4 requests, and expect 429 + + // keep sending requests till get 200 first, that will cost one 200 + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectOkResp) + + // fire the rest of the requests + if err := GotExactExpectedResponse(t, 2, suite.RoundTripper, expectOkReq, expectOkResp); err != nil { + t.Errorf("failed to get expected response for the first three requests: %v", err) + } + if err := GotExactExpectedResponse(t, 1, suite.RoundTripper, expectLimitReq, expectLimitResp); err != nil { + t.Errorf("failed to get expected response for the last (fourth) request: %v", err) + } + }) + + t.Run("only partly matched headers cannot got limited", func(t *testing.T) { + requestHeaders := map[string]string{ + "x-user-id": "one", + } + + // it does not require any rate limit header, since this request never be rate limited. + expectOkResp := http.ExpectedResponse{ + Request: http.Request{ + Path: "/get", + Headers: requestHeaders, + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + expectOkReq := http.MakeRequest(t, &expectOkResp, gwAddr, "HTTP", "http") + + // send exactly 4 requests, and still expect 200 + + // keep sending requests till get 200 first, that will cost one 200 + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectOkResp) + + // fire the rest of the requests + if err := GotExactExpectedResponse(t, 3, suite.RoundTripper, expectOkReq, expectOkResp); err != nil { + t.Errorf("failed to get expected responses for the request: %v", err) + } + }) + + t.Run("only matched cidr cannot got limited", func(t *testing.T) { + // it does not require any rate limit header, since this request never be rate limited. + expectOkResp := http.ExpectedResponse{ + Request: http.Request{ + Path: "/get", + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + expectOkReq := http.MakeRequest(t, &expectOkResp, gwAddr, "HTTP", "http") + + // send exactly 4 requests, and still expect 200 + + // keep sending requests till get 200 first, that will cost one 200 + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectOkResp) + + // fire the rest of the requests + if err := GotExactExpectedResponse(t, 3, suite.RoundTripper, expectOkReq, expectOkResp); err != nil { + t.Errorf("failed to get expected responses for the request: %v", err) + } + }) + }, +} + func GotExactExpectedResponse(t *testing.T, n int, r roundtripper.RoundTripper, req roundtripper.Request, resp http.ExpectedResponse) error { for i := 0; i < n; i++ { cReq, cRes, err := r.CaptureRoundTrip(req) From deddeb472fa7307bf6e83f782d02ec2eba828bf1 Mon Sep 17 00:00:00 2001 From: sh2 Date: Tue, 22 Oct 2024 05:44:31 +0800 Subject: [PATCH 46/56] chore: clean up gatewayapi v1alpha2 helpers (#4482) clean up gatewayapi v1alpha2 helpers Signed-off-by: shawnh2 Co-authored-by: Huabing Zhao --- internal/gatewayapi/helpers_v1alpha2.go | 45 ------------------------- internal/provider/kubernetes/routes.go | 9 ++--- 2 files changed, 3 insertions(+), 51 deletions(-) delete mode 100644 internal/gatewayapi/helpers_v1alpha2.go diff --git a/internal/gatewayapi/helpers_v1alpha2.go b/internal/gatewayapi/helpers_v1alpha2.go deleted file mode 100644 index 3b1dffde66f..00000000000 --- a/internal/gatewayapi/helpers_v1alpha2.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright Envoy Gateway Authors -// SPDX-License-Identifier: Apache-2.0 -// The full text of the Apache license is available in the LICENSE file at -// the root of the repo. - -// This file contains code derived from Contour, -// https://github.com/projectcontour/contour -// and is provided here subject to the following: -// Copyright Project Contour Authors -// SPDX-License-Identifier: Apache-2.0 - -package gatewayapi - -import ( - gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" - gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" -) - -// TODO: [gwapiv1a2-gwapiv1] -// This file can be removed once all routes graduates to gwapiv1. - -// UpgradeBackendRef converts gwapiv1a2.BackendRef to gwapiv1.BackendRef -func UpgradeBackendRef(old gwapiv1a2.BackendRef) gwapiv1.BackendRef { - upgraded := gwapiv1.BackendRef{} - - if old.Group != nil { - upgraded.Group = GroupPtr(string(*old.Group)) - } - - if old.Kind != nil { - upgraded.Kind = KindPtr(string(*old.Kind)) - } - - if old.Namespace != nil { - upgraded.Namespace = NamespacePtr(string(*old.Namespace)) - } - - upgraded.Name = old.Name - - if old.Port != nil { - upgraded.Port = PortNumPtr(int32(*old.Port)) - } - - return upgraded -} diff --git a/internal/provider/kubernetes/routes.go b/internal/provider/kubernetes/routes.go index 3a0a9f8131e..956a2b59b3f 100644 --- a/internal/provider/kubernetes/routes.go +++ b/internal/provider/kubernetes/routes.go @@ -56,8 +56,7 @@ func (r *gatewayAPIReconciler) processTLSRoutes(ctx context.Context, gatewayName for _, rule := range tlsRoute.Spec.Rules { for _, backendRef := range rule.BackendRefs { - ref := gatewayapi.UpgradeBackendRef(backendRef) - if err := validateBackendRef(&ref); err != nil { + if err := validateBackendRef(&backendRef); err != nil { r.log.Error(err, "invalid backendRef") continue } @@ -467,8 +466,7 @@ func (r *gatewayAPIReconciler) processTCPRoutes(ctx context.Context, gatewayName for _, rule := range tcpRoute.Spec.Rules { for _, backendRef := range rule.BackendRefs { - ref := gatewayapi.UpgradeBackendRef(backendRef) - if err := validateBackendRef(&ref); err != nil { + if err := validateBackendRef(&backendRef); err != nil { r.log.Error(err, "invalid backendRef") continue } @@ -545,8 +543,7 @@ func (r *gatewayAPIReconciler) processUDPRoutes(ctx context.Context, gatewayName for _, rule := range udpRoute.Spec.Rules { for _, backendRef := range rule.BackendRefs { - ref := gatewayapi.UpgradeBackendRef(backendRef) - if err := validateBackendRef(&ref); err != nil { + if err := validateBackendRef(&backendRef); err != nil { r.log.Error(err, "invalid backendRef") continue } From 33ac6ca6873bac05cee79f7a177c18caa45506e2 Mon Sep 17 00:00:00 2001 From: Hartigan Date: Tue, 22 Oct 2024 01:23:37 +0200 Subject: [PATCH 47/56] Docs/Tests: documentation and e2e tests for Datadog tracing (#4480) * Add e2e test for Datadog tracing Signed-off-by: Hartigan * Add documentation for Datadog tracing support for proxy Signed-off-by: Hartigan * Format test yaml files Signed-off-by: Hartigan * Remove Grafana Alloy for Datadog tracing e2e test Signed-off-by: Hartigan * Update generated files Signed-off-by: Hartigan --------- Signed-off-by: Hartigan --- charts/gateway-addons-helm/Chart.lock | 6 +- charts/gateway-addons-helm/Chart.yaml | 2 +- charts/gateway-addons-helm/README.md | 14 ++- charts/gateway-addons-helm/values.yaml | 8 +- .../latest/install/gateway-addons-helm-api.md | 14 ++- .../latest/tasks/observability/proxy-trace.md | 62 ++++++++++++- .../latest/install/gateway-addons-helm-api.md | 14 ++- test/e2e/testdata/tracing-datadog.yaml | 91 +++++++++++++++++++ test/e2e/tests/tracing.go | 67 +++++++++++++- test/helm/gateway-addons-helm/e2e.out.yaml | 38 ++++---- 10 files changed, 277 insertions(+), 39 deletions(-) create mode 100644 test/e2e/testdata/tracing-datadog.yaml diff --git a/charts/gateway-addons-helm/Chart.lock b/charts/gateway-addons-helm/Chart.lock index 4b6f92ac77c..228a952fdc1 100644 --- a/charts/gateway-addons-helm/Chart.lock +++ b/charts/gateway-addons-helm/Chart.lock @@ -16,6 +16,6 @@ dependencies: version: 1.3.1 - name: opentelemetry-collector repository: https://open-telemetry.github.io/opentelemetry-helm-charts - version: 0.73.1 -digest: sha256:4c16df8d7efc27aff566fa5dfd2eba6527adbf3fc8e94e7e3ccfc0cee7836f1c -generated: "2024-06-20T11:46:59.148579+08:00" + version: 0.108.0 +digest: sha256:ea6663bb1358123b96b69d2c5b0b8c20650a43dc39b24c482f0560201fd2cc3a +generated: "2024-10-19T12:59:47.251089661+02:00" diff --git a/charts/gateway-addons-helm/Chart.yaml b/charts/gateway-addons-helm/Chart.yaml index 84ac6228f62..2571ccec51e 100644 --- a/charts/gateway-addons-helm/Chart.yaml +++ b/charts/gateway-addons-helm/Chart.yaml @@ -47,5 +47,5 @@ dependencies: condition: tempo.enabled - name: opentelemetry-collector repository: https://open-telemetry.github.io/opentelemetry-helm-charts - version: 0.73.1 + version: 0.108.0 condition: opentelemetry-collector.enabled diff --git a/charts/gateway-addons-helm/README.md b/charts/gateway-addons-helm/README.md index ccbd26b983d..a52af3e2d14 100644 --- a/charts/gateway-addons-helm/README.md +++ b/charts/gateway-addons-helm/README.md @@ -25,7 +25,7 @@ An Add-ons Helm chart for Envoy Gateway | https://grafana.github.io/helm-charts | grafana | 8.0.0 | | https://grafana.github.io/helm-charts | loki | 4.8.0 | | https://grafana.github.io/helm-charts | tempo | 1.3.1 | -| https://open-telemetry.github.io/opentelemetry-helm-charts | opentelemetry-collector | 0.73.1 | +| https://open-telemetry.github.io/opentelemetry-helm-charts | opentelemetry-collector | 0.108.0 | | https://prometheus-community.github.io/helm-charts | prometheus | 25.21.0 | ## Usage @@ -103,7 +103,7 @@ To uninstall the chart: | loki.singleBinary.replicas | int | `1` | | | loki.test.enabled | bool | `false` | | | loki.write.replicas | int | `0` | | -| opentelemetry-collector.config.exporters.logging.verbosity | string | `"detailed"` | | +| opentelemetry-collector.config.exporters.debug.verbosity | string | `"detailed"` | | | opentelemetry-collector.config.exporters.loki.endpoint | string | `"http://loki.monitoring.svc:3100/loki/api/v1/push"` | | | opentelemetry-collector.config.exporters.otlp.endpoint | string | `"tempo.monitoring.svc:4317"` | | | opentelemetry-collector.config.exporters.otlp.tls.insecure | bool | `true` | | @@ -112,6 +112,7 @@ To uninstall the chart: | opentelemetry-collector.config.processors.attributes.actions[0].action | string | `"insert"` | | | opentelemetry-collector.config.processors.attributes.actions[0].key | string | `"loki.attribute.labels"` | | | opentelemetry-collector.config.processors.attributes.actions[0].value | string | `"k8s.pod.name, k8s.namespace.name"` | | +| opentelemetry-collector.config.receivers.datadog.endpoint | string | `"${env:MY_POD_IP}:8126"` | | | opentelemetry-collector.config.receivers.otlp.protocols.grpc.endpoint | string | `"${env:MY_POD_IP}:4317"` | | | opentelemetry-collector.config.receivers.otlp.protocols.http.endpoint | string | `"${env:MY_POD_IP}:4318"` | | | opentelemetry-collector.config.receivers.zipkin.endpoint | string | `"${env:MY_POD_IP}:9411"` | | @@ -120,12 +121,15 @@ To uninstall the chart: | opentelemetry-collector.config.service.pipelines.logs.processors[0] | string | `"attributes"` | | | opentelemetry-collector.config.service.pipelines.logs.receivers[0] | string | `"otlp"` | | | opentelemetry-collector.config.service.pipelines.metrics.exporters[0] | string | `"prometheus"` | | -| opentelemetry-collector.config.service.pipelines.metrics.receivers[0] | string | `"otlp"` | | +| opentelemetry-collector.config.service.pipelines.metrics.receivers[0] | string | `"datadog"` | | +| opentelemetry-collector.config.service.pipelines.metrics.receivers[1] | string | `"otlp"` | | | opentelemetry-collector.config.service.pipelines.traces.exporters[0] | string | `"otlp"` | | -| opentelemetry-collector.config.service.pipelines.traces.receivers[0] | string | `"otlp"` | | -| opentelemetry-collector.config.service.pipelines.traces.receivers[1] | string | `"zipkin"` | | +| opentelemetry-collector.config.service.pipelines.traces.receivers[0] | string | `"datadog"` | | +| opentelemetry-collector.config.service.pipelines.traces.receivers[1] | string | `"otlp"` | | +| opentelemetry-collector.config.service.pipelines.traces.receivers[2] | string | `"zipkin"` | | | opentelemetry-collector.enabled | bool | `false` | | | opentelemetry-collector.fullnameOverride | string | `"otel-collector"` | | +| opentelemetry-collector.image.repository | string | `"otel/opentelemetry-collector-contrib"` | | | opentelemetry-collector.mode | string | `"deployment"` | | | prometheus.alertmanager.enabled | bool | `false` | | | prometheus.enabled | bool | `true` | | diff --git a/charts/gateway-addons-helm/values.yaml b/charts/gateway-addons-helm/values.yaml index 55a02b68255..d3fb043ddd4 100644 --- a/charts/gateway-addons-helm/values.yaml +++ b/charts/gateway-addons-helm/values.yaml @@ -181,11 +181,13 @@ opentelemetry-collector: enabled: false fullnameOverride: otel-collector mode: deployment + image: + repository: "otel/opentelemetry-collector-contrib" config: exporters: prometheus: endpoint: 0.0.0.0:19001 - logging: + debug: verbosity: detailed loki: endpoint: "http://loki.monitoring.svc:3100/loki/api/v1/push" @@ -207,6 +209,8 @@ opentelemetry-collector: # Loki will convert this to k8s_pod_name label. value: k8s.pod.name, k8s.namespace.name receivers: + datadog: + endpoint: ${env:MY_POD_IP}:8126 zipkin: endpoint: ${env:MY_POD_IP}:9411 otlp: @@ -223,6 +227,7 @@ opentelemetry-collector: exporters: - prometheus receivers: + - datadog - otlp logs: exporters: @@ -235,5 +240,6 @@ opentelemetry-collector: exporters: - otlp receivers: + - datadog - otlp - zipkin diff --git a/site/content/en/latest/install/gateway-addons-helm-api.md b/site/content/en/latest/install/gateway-addons-helm-api.md index a0ae0ed62f6..9835e21cd62 100644 --- a/site/content/en/latest/install/gateway-addons-helm-api.md +++ b/site/content/en/latest/install/gateway-addons-helm-api.md @@ -27,7 +27,7 @@ An Add-ons Helm chart for Envoy Gateway | https://grafana.github.io/helm-charts | grafana | 8.0.0 | | https://grafana.github.io/helm-charts | loki | 4.8.0 | | https://grafana.github.io/helm-charts | tempo | 1.3.1 | -| https://open-telemetry.github.io/opentelemetry-helm-charts | opentelemetry-collector | 0.73.1 | +| https://open-telemetry.github.io/opentelemetry-helm-charts | opentelemetry-collector | 0.108.0 | | https://prometheus-community.github.io/helm-charts | prometheus | 25.21.0 | ## Values @@ -82,7 +82,7 @@ An Add-ons Helm chart for Envoy Gateway | loki.singleBinary.replicas | int | `1` | | | loki.test.enabled | bool | `false` | | | loki.write.replicas | int | `0` | | -| opentelemetry-collector.config.exporters.logging.verbosity | string | `"detailed"` | | +| opentelemetry-collector.config.exporters.debug.verbosity | string | `"detailed"` | | | opentelemetry-collector.config.exporters.loki.endpoint | string | `"http://loki.monitoring.svc:3100/loki/api/v1/push"` | | | opentelemetry-collector.config.exporters.otlp.endpoint | string | `"tempo.monitoring.svc:4317"` | | | opentelemetry-collector.config.exporters.otlp.tls.insecure | bool | `true` | | @@ -91,6 +91,7 @@ An Add-ons Helm chart for Envoy Gateway | opentelemetry-collector.config.processors.attributes.actions[0].action | string | `"insert"` | | | opentelemetry-collector.config.processors.attributes.actions[0].key | string | `"loki.attribute.labels"` | | | opentelemetry-collector.config.processors.attributes.actions[0].value | string | `"k8s.pod.name, k8s.namespace.name"` | | +| opentelemetry-collector.config.receivers.datadog.endpoint | string | `"${env:MY_POD_IP}:8126"` | | | opentelemetry-collector.config.receivers.otlp.protocols.grpc.endpoint | string | `"${env:MY_POD_IP}:4317"` | | | opentelemetry-collector.config.receivers.otlp.protocols.http.endpoint | string | `"${env:MY_POD_IP}:4318"` | | | opentelemetry-collector.config.receivers.zipkin.endpoint | string | `"${env:MY_POD_IP}:9411"` | | @@ -99,12 +100,15 @@ An Add-ons Helm chart for Envoy Gateway | opentelemetry-collector.config.service.pipelines.logs.processors[0] | string | `"attributes"` | | | opentelemetry-collector.config.service.pipelines.logs.receivers[0] | string | `"otlp"` | | | opentelemetry-collector.config.service.pipelines.metrics.exporters[0] | string | `"prometheus"` | | -| opentelemetry-collector.config.service.pipelines.metrics.receivers[0] | string | `"otlp"` | | +| opentelemetry-collector.config.service.pipelines.metrics.receivers[0] | string | `"datadog"` | | +| opentelemetry-collector.config.service.pipelines.metrics.receivers[1] | string | `"otlp"` | | | opentelemetry-collector.config.service.pipelines.traces.exporters[0] | string | `"otlp"` | | -| opentelemetry-collector.config.service.pipelines.traces.receivers[0] | string | `"otlp"` | | -| opentelemetry-collector.config.service.pipelines.traces.receivers[1] | string | `"zipkin"` | | +| opentelemetry-collector.config.service.pipelines.traces.receivers[0] | string | `"datadog"` | | +| opentelemetry-collector.config.service.pipelines.traces.receivers[1] | string | `"otlp"` | | +| opentelemetry-collector.config.service.pipelines.traces.receivers[2] | string | `"zipkin"` | | | opentelemetry-collector.enabled | bool | `false` | | | opentelemetry-collector.fullnameOverride | string | `"otel-collector"` | | +| opentelemetry-collector.image.repository | string | `"otel/opentelemetry-collector-contrib"` | | | opentelemetry-collector.mode | string | `"deployment"` | | | prometheus.alertmanager.enabled | bool | `false` | | | prometheus.enabled | bool | `true` | | diff --git a/site/content/en/latest/tasks/observability/proxy-trace.md b/site/content/en/latest/tasks/observability/proxy-trace.md index ddaf68e415a..39243d329bc 100644 --- a/site/content/en/latest/tasks/observability/proxy-trace.md +++ b/site/content/en/latest/tasks/observability/proxy-trace.md @@ -19,7 +19,7 @@ TEMPO_IP=$(kubectl get svc tempo -n monitoring -o jsonpath='{.status.loadBalance By default, Envoy Gateway doesn't send traces to any sink. You can enable traces by setting the `telemetry.tracing` in the [EnvoyProxy][envoy-proxy-crd] CRD. -Currently, Envoy Gateway support OpenTelemetry and [Zipkin](../../api/extension_types#zipkintracingprovider) tracer. +Currently, Envoy Gateway support OpenTelemetry, [Zipkin](../../api/extension_types#zipkintracingprovider) and Datadog tracer. ### Tracing Provider @@ -155,6 +155,66 @@ Verify zipkin traces from tempo: curl -s "http://$TEMPO_IP:3100/api/search?tags=component%3Dproxy+provider%3Dzipkin" | jq .traces ``` +{{% /tab %}} +{{% tab header="Datadog" %}} + +```shell +kubectl apply -f - <}} diff --git a/site/content/zh/latest/install/gateway-addons-helm-api.md b/site/content/zh/latest/install/gateway-addons-helm-api.md index a0ae0ed62f6..9835e21cd62 100644 --- a/site/content/zh/latest/install/gateway-addons-helm-api.md +++ b/site/content/zh/latest/install/gateway-addons-helm-api.md @@ -27,7 +27,7 @@ An Add-ons Helm chart for Envoy Gateway | https://grafana.github.io/helm-charts | grafana | 8.0.0 | | https://grafana.github.io/helm-charts | loki | 4.8.0 | | https://grafana.github.io/helm-charts | tempo | 1.3.1 | -| https://open-telemetry.github.io/opentelemetry-helm-charts | opentelemetry-collector | 0.73.1 | +| https://open-telemetry.github.io/opentelemetry-helm-charts | opentelemetry-collector | 0.108.0 | | https://prometheus-community.github.io/helm-charts | prometheus | 25.21.0 | ## Values @@ -82,7 +82,7 @@ An Add-ons Helm chart for Envoy Gateway | loki.singleBinary.replicas | int | `1` | | | loki.test.enabled | bool | `false` | | | loki.write.replicas | int | `0` | | -| opentelemetry-collector.config.exporters.logging.verbosity | string | `"detailed"` | | +| opentelemetry-collector.config.exporters.debug.verbosity | string | `"detailed"` | | | opentelemetry-collector.config.exporters.loki.endpoint | string | `"http://loki.monitoring.svc:3100/loki/api/v1/push"` | | | opentelemetry-collector.config.exporters.otlp.endpoint | string | `"tempo.monitoring.svc:4317"` | | | opentelemetry-collector.config.exporters.otlp.tls.insecure | bool | `true` | | @@ -91,6 +91,7 @@ An Add-ons Helm chart for Envoy Gateway | opentelemetry-collector.config.processors.attributes.actions[0].action | string | `"insert"` | | | opentelemetry-collector.config.processors.attributes.actions[0].key | string | `"loki.attribute.labels"` | | | opentelemetry-collector.config.processors.attributes.actions[0].value | string | `"k8s.pod.name, k8s.namespace.name"` | | +| opentelemetry-collector.config.receivers.datadog.endpoint | string | `"${env:MY_POD_IP}:8126"` | | | opentelemetry-collector.config.receivers.otlp.protocols.grpc.endpoint | string | `"${env:MY_POD_IP}:4317"` | | | opentelemetry-collector.config.receivers.otlp.protocols.http.endpoint | string | `"${env:MY_POD_IP}:4318"` | | | opentelemetry-collector.config.receivers.zipkin.endpoint | string | `"${env:MY_POD_IP}:9411"` | | @@ -99,12 +100,15 @@ An Add-ons Helm chart for Envoy Gateway | opentelemetry-collector.config.service.pipelines.logs.processors[0] | string | `"attributes"` | | | opentelemetry-collector.config.service.pipelines.logs.receivers[0] | string | `"otlp"` | | | opentelemetry-collector.config.service.pipelines.metrics.exporters[0] | string | `"prometheus"` | | -| opentelemetry-collector.config.service.pipelines.metrics.receivers[0] | string | `"otlp"` | | +| opentelemetry-collector.config.service.pipelines.metrics.receivers[0] | string | `"datadog"` | | +| opentelemetry-collector.config.service.pipelines.metrics.receivers[1] | string | `"otlp"` | | | opentelemetry-collector.config.service.pipelines.traces.exporters[0] | string | `"otlp"` | | -| opentelemetry-collector.config.service.pipelines.traces.receivers[0] | string | `"otlp"` | | -| opentelemetry-collector.config.service.pipelines.traces.receivers[1] | string | `"zipkin"` | | +| opentelemetry-collector.config.service.pipelines.traces.receivers[0] | string | `"datadog"` | | +| opentelemetry-collector.config.service.pipelines.traces.receivers[1] | string | `"otlp"` | | +| opentelemetry-collector.config.service.pipelines.traces.receivers[2] | string | `"zipkin"` | | | opentelemetry-collector.enabled | bool | `false` | | | opentelemetry-collector.fullnameOverride | string | `"otel-collector"` | | +| opentelemetry-collector.image.repository | string | `"otel/opentelemetry-collector-contrib"` | | | opentelemetry-collector.mode | string | `"deployment"` | | | prometheus.alertmanager.enabled | bool | `false` | | | prometheus.enabled | bool | `true` | | diff --git a/test/e2e/testdata/tracing-datadog.yaml b/test/e2e/testdata/tracing-datadog.yaml new file mode 100644 index 00000000000..e4f54a7eebe --- /dev/null +++ b/test/e2e/testdata/tracing-datadog.yaml @@ -0,0 +1,91 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: eg-special-case-datadog + namespace: gateway-conformance-infra +spec: + gatewayClassName: "{GATEWAY_CLASS_NAME}" + listeners: + - name: http + port: 80 + protocol: HTTP + allowedRoutes: + namespaces: + from: All + infrastructure: + parametersRef: + group: gateway.envoyproxy.io + kind: EnvoyProxy + name: datadog-tracing +--- +apiVersion: v1 +kind: Service +metadata: + name: datadog-agent + namespace: monitoring +spec: + selector: + app.kubernetes.io/instance: eg-addons + app.kubernetes.io/name: opentelemetry-collector + component: standalone-collector + ports: + - protocol: TCP + port: 8126 + targetPort: 8126 +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: EnvoyProxy +metadata: + name: datadog-tracing + namespace: gateway-conformance-infra +spec: + logging: + level: + default: debug + telemetry: + tracing: + provider: + type: Datadog + backendRefs: + - name: datadog-agent + namespace: monitoring + port: 8126 + customTags: + "provider": + type: Literal + literal: + value: "datadog" + "k8s.cluster.name": + type: Literal + literal: + value: "envoy-gateway" + "k8s.pod.name": + type: Environment + environment: + name: ENVOY_POD_NAME + defaultValue: "-" + "k8s.namespace.name": + type: Environment + environment: + name: ENVOY_GATEWAY_NAMESPACE + defaultValue: "envoy-gateway-system" + shutdown: + drainTimeout: 5s + minDrainDuration: 1s +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: tracing-datadog + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: eg-special-case-datadog + rules: + - matches: + - path: + type: PathPrefix + value: /datadog + backendRefs: + - name: infra-backend-v2 + port: 8080 diff --git a/test/e2e/tests/tracing.go b/test/e2e/tests/tracing.go index 5ead14b48a1..93e4cb23897 100644 --- a/test/e2e/tests/tracing.go +++ b/test/e2e/tests/tracing.go @@ -24,7 +24,7 @@ import ( ) func init() { - ConformanceTests = append(ConformanceTests, OpenTelemetryTracingTest, ZipkinTracingTest) + ConformanceTests = append(ConformanceTests, OpenTelemetryTracingTest, ZipkinTracingTest, DatadogTracingTest) } var OpenTelemetryTracingTest = suite.ConformanceTest{ @@ -141,3 +141,68 @@ var ZipkinTracingTest = suite.ConformanceTest{ }) }, } + +var DatadogTracingTest = suite.ConformanceTest{ + ShortName: "DatadogTracing", + Description: "Make sure Datadog tracing is working", + Manifests: []string{"testdata/tracing-datadog.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("tempo", func(t *testing.T) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "tracing-datadog", Namespace: ns} + gwNN := types.NamespacedName{Name: "eg-special-case-datadog", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + expectedResponse := httputils.ExpectedResponse{ + Request: httputils.Request{ + Path: "/datadog", + }, + Response: httputils.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + // make sure listener is ready + httputils.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse) + + tags := map[string]string{ + "component": "proxy", + "provider": "datadog", + "service.name": fmt.Sprintf("%s.%s", gwNN.Name, gwNN.Namespace), + } + if err := wait.PollUntilContextTimeout(context.TODO(), time.Second, time.Minute, true, + func(ctx context.Context) (bool, error) { + preCount, err := QueryTraceFromTempo(t, suite.Client, tags) + if err != nil { + tlog.Logf(t, "failed to get trace count from tempo: %v", err) + return false, nil + } + + httputils.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse) + + // looks like we need almost 15 seconds to get the trace from Tempo? + err = wait.PollUntilContextTimeout(context.TODO(), time.Second, 60*time.Second, true, func(ctx context.Context) (done bool, err error) { + curCount, err := QueryTraceFromTempo(t, suite.Client, tags) + if err != nil { + tlog.Logf(t, "failed to get curCount count from tempo: %v", err) + return false, nil + } + + if curCount > preCount { + return true, nil + } + + return false, nil + }) + if err != nil { + tlog.Logf(t, "failed to get current count from tempo: %v", err) + return false, nil + } + + return true, nil + }); err != nil { + t.Errorf("failed to get trace from tempo: %v", err) + } + }) + }, +} diff --git a/test/helm/gateway-addons-helm/e2e.out.yaml b/test/helm/gateway-addons-helm/e2e.out.yaml index 15445239f3d..1e7c8fda8ff 100644 --- a/test/helm/gateway-addons-helm/e2e.out.yaml +++ b/test/helm/gateway-addons-helm/e2e.out.yaml @@ -32,10 +32,10 @@ metadata: name: otel-collector namespace: monitoring labels: - helm.sh/chart: opentelemetry-collector-0.73.1 + helm.sh/chart: opentelemetry-collector-0.108.0 app.kubernetes.io/name: opentelemetry-collector app.kubernetes.io/instance: gateway-addons-helm - app.kubernetes.io/version: "0.88.0" + app.kubernetes.io/version: "0.111.0" app.kubernetes.io/managed-by: Helm --- # Source: gateway-addons-helm/charts/prometheus/templates/serviceaccount.yaml @@ -219,16 +219,16 @@ metadata: name: otel-collector namespace: monitoring labels: - helm.sh/chart: opentelemetry-collector-0.73.1 + helm.sh/chart: opentelemetry-collector-0.108.0 app.kubernetes.io/name: opentelemetry-collector app.kubernetes.io/instance: gateway-addons-helm - app.kubernetes.io/version: "0.88.0" + app.kubernetes.io/version: "0.111.0" app.kubernetes.io/managed-by: Helm + data: relay: | exporters: - debug: {} - logging: + debug: verbosity: detailed loki: endpoint: http://loki.monitoring.svc:3100/loki/api/v1/push @@ -239,9 +239,8 @@ data: prometheus: endpoint: 0.0.0.0:19001 extensions: - health_check: {} - memory_ballast: - size_in_percentage: 40 + health_check: + endpoint: ${env:MY_POD_IP}:13133 processors: attributes: actions: @@ -254,6 +253,8 @@ data: limit_percentage: 80 spike_limit_percentage: 25 receivers: + datadog: + endpoint: ${env:MY_POD_IP}:8126 jaeger: protocols: grpc: @@ -296,6 +297,7 @@ data: - memory_limiter - batch receivers: + - datadog - otlp traces: exporters: @@ -304,6 +306,7 @@ data: - memory_limiter - batch receivers: + - datadog - otlp - zipkin telemetry: @@ -9517,11 +9520,12 @@ metadata: name: otel-collector namespace: monitoring labels: - helm.sh/chart: opentelemetry-collector-0.73.1 + helm.sh/chart: opentelemetry-collector-0.108.0 app.kubernetes.io/name: opentelemetry-collector app.kubernetes.io/instance: gateway-addons-helm - app.kubernetes.io/version: "0.88.0" + app.kubernetes.io/version: "0.111.0" app.kubernetes.io/managed-by: Helm + component: standalone-collector spec: type: ClusterIP @@ -9733,11 +9737,12 @@ metadata: name: otel-collector namespace: monitoring labels: - helm.sh/chart: opentelemetry-collector-0.73.1 + helm.sh/chart: opentelemetry-collector-0.108.0 app.kubernetes.io/name: opentelemetry-collector app.kubernetes.io/instance: gateway-addons-helm - app.kubernetes.io/version: "0.88.0" + app.kubernetes.io/version: "0.111.0" app.kubernetes.io/managed-by: Helm + spec: replicas: 1 revisionHistoryLimit: 10 @@ -9751,7 +9756,7 @@ spec: template: metadata: annotations: - checksum/config: 4eb06aca6ff4da4de927cb9ba7d8ceb883d2484011fbd670683037b8ea4d996c + checksum/config: 270a8503091b51a264317115cf6df46b4501b03fc135eca95b93dca57a522a70 labels: app.kubernetes.io/name: opentelemetry-collector @@ -9765,12 +9770,11 @@ spec: {} containers: - name: opentelemetry-collector - command: - - /otelcol-contrib + args: - --config=/conf/relay.yaml securityContext: {} - image: "otel/opentelemetry-collector-contrib:0.88.0" + image: "otel/opentelemetry-collector-contrib:0.111.0" imagePullPolicy: IfNotPresent ports: From 04fc944f2cae7e15c23167c581389c958f4eaea1 Mon Sep 17 00:00:00 2001 From: Huabing Zhao Date: Tue, 22 Oct 2024 08:50:25 +0800 Subject: [PATCH 48/56] impl: custom error response (#4415) * gateway api translation Signed-off-by: Huabing Zhao * add tests for gateway api translation Signed-off-by: Huabing Zhao * xds translation Signed-off-by: Huabing Zhao * test for xds translation Signed-off-by: Huabing Zhao * fix lint Signed-off-by: Huabing Zhao * fix lint Signed-off-by: Huabing Zhao * e2e test Signed-off-by: Huabing Zhao * add cel validation Signed-off-by: Huabing Zhao * fix test Signed-off-by: Huabing Zhao * fix test Signed-off-by: Huabing Zhao * fix test Signed-off-by: Huabing Zhao * fix lint Signed-off-by: Huabing Zhao * address comment Signed-off-by: Huabing Zhao * address comment Signed-off-by: Huabing Zhao * fix lint Signed-off-by: Huabing Zhao * make Body required Signed-off-by: Huabing Zhao * fix e2e Signed-off-by: Huabing Zhao --------- Signed-off-by: Huabing Zhao --- api/v1alpha1/backendtrafficpolicy_types.go | 1 - api/v1alpha1/envoyproxy_types.go | 7 +- api/v1alpha1/shared_types.go | 47 +- api/v1alpha1/zz_generated.deepcopy.go | 12 +- ....envoyproxy.io_backendtrafficpolicies.yaml | 70 ++- .../gateway.envoyproxy.io_envoyproxies.yaml | 5 + ...ateway.envoyproxy.io_httproutefilters.yaml | 29 +- go.mod | 2 +- internal/gatewayapi/backendtrafficpolicy.go | 133 +++++- ...response-override-invalid-valueref.in.yaml | 141 ++++++ ...esponse-override-invalid-valueref.out.yaml | 371 +++++++++++++++ ...afficpolicy-with-response-override.in.yaml | 145 ++++++ ...fficpolicy-with-response-override.out.yaml | 414 ++++++++++++++++ internal/gatewayapi/translator.go | 2 +- internal/ir/xds.go | 59 +++ internal/ir/zz_generated.deepcopy.go | 111 +++++ internal/provider/kubernetes/controller.go | 45 +- internal/provider/kubernetes/indexers.go | 31 ++ internal/provider/kubernetes/predicates.go | 22 +- internal/xds/translator/custom_response.go | 450 ++++++++++++++++++ internal/xds/translator/oidc.go | 4 +- .../testdata/in/xds-ir/custom-response.yaml | 56 +++ .../out/xds-ir/custom-response.clusters.yaml | 17 + .../out/xds-ir/custom-response.endpoints.yaml | 12 + .../out/xds-ir/custom-response.listeners.yaml | 130 +++++ .../out/xds-ir/custom-response.routes.yaml | 33 ++ site/content/en/latest/api/extension_types.md | 28 +- site/content/zh/latest/api/extension_types.md | 28 +- .../backendtrafficpolicy_test.go | 245 ++++++++++ test/e2e/testdata/response-override.yaml | 62 +++ test/e2e/tests/response-override.go | 83 ++++ 31 files changed, 2712 insertions(+), 83 deletions(-) create mode 100644 internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override-invalid-valueref.in.yaml create mode 100644 internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override-invalid-valueref.out.yaml create mode 100644 internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.in.yaml create mode 100644 internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml create mode 100644 internal/xds/translator/custom_response.go create mode 100644 internal/xds/translator/testdata/in/xds-ir/custom-response.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/custom-response.clusters.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/custom-response.endpoints.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/custom-response.listeners.yaml create mode 100644 internal/xds/translator/testdata/out/xds-ir/custom-response.routes.yaml create mode 100644 test/e2e/testdata/response-override.yaml create mode 100644 test/e2e/tests/response-override.go diff --git a/api/v1alpha1/backendtrafficpolicy_types.go b/api/v1alpha1/backendtrafficpolicy_types.go index 4e6118e7035..4183c12830f 100644 --- a/api/v1alpha1/backendtrafficpolicy_types.go +++ b/api/v1alpha1/backendtrafficpolicy_types.go @@ -74,7 +74,6 @@ type BackendTrafficPolicySpec struct { // If multiple configurations are specified, the first one to match wins. // // +optional - // +notImplementedHide ResponseOverride []*ResponseOverride `json:"responseOverride,omitempty"` } diff --git a/api/v1alpha1/envoyproxy_types.go b/api/v1alpha1/envoyproxy_types.go index d7a2a73abe8..4bf7920f624 100644 --- a/api/v1alpha1/envoyproxy_types.go +++ b/api/v1alpha1/envoyproxy_types.go @@ -124,6 +124,8 @@ type EnvoyProxySpec struct { // // - envoy.filters.http.ratelimit // + // - envoy.filters.http.custom_response + // // - envoy.filters.http.router // // Note: "envoy.filters.http.router" cannot be reordered, it's always the last filter in the chain. @@ -174,7 +176,7 @@ type FilterPosition struct { } // EnvoyFilter defines the type of Envoy HTTP filter. -// +kubebuilder:validation:Enum=envoy.filters.http.health_check;envoy.filters.http.fault;envoy.filters.http.cors;envoy.filters.http.ext_authz;envoy.filters.http.basic_auth;envoy.filters.http.oauth2;envoy.filters.http.jwt_authn;envoy.filters.http.stateful_session;envoy.filters.http.ext_proc;envoy.filters.http.wasm;envoy.filters.http.rbac;envoy.filters.http.local_ratelimit;envoy.filters.http.ratelimit +// +kubebuilder:validation:Enum=envoy.filters.http.health_check;envoy.filters.http.fault;envoy.filters.http.cors;envoy.filters.http.ext_authz;envoy.filters.http.basic_auth;envoy.filters.http.oauth2;envoy.filters.http.jwt_authn;envoy.filters.http.stateful_session;envoy.filters.http.ext_proc;envoy.filters.http.wasm;envoy.filters.http.rbac;envoy.filters.http.local_ratelimit;envoy.filters.http.ratelimit;envoy.filters.http.custom_response type EnvoyFilter string const ( @@ -217,6 +219,9 @@ const ( // EnvoyFilterRateLimit defines the Envoy HTTP rate limit filter. EnvoyFilterRateLimit EnvoyFilter = "envoy.filters.http.ratelimit" + // EnvoyFilterCustomResponse defines the Envoy HTTP custom response filter. + EnvoyFilterCustomResponse EnvoyFilter = "envoy.filters.http.custom_response" + // EnvoyFilterRouter defines the Envoy HTTP router filter. EnvoyFilterRouter EnvoyFilter = "envoy.filters.http.router" ) diff --git a/api/v1alpha1/shared_types.go b/api/v1alpha1/shared_types.go index fe795c833db..617151e296a 100644 --- a/api/v1alpha1/shared_types.go +++ b/api/v1alpha1/shared_types.go @@ -627,33 +627,48 @@ type ResponseOverride struct { // CustomResponseMatch defines the configuration for matching a user response to return a custom one. type CustomResponseMatch struct { // Status code to match on. The match evaluates to true if any of the matches are successful. - StatusCode []StatusCodeMatch `json:"statusCode"` + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=50 + StatusCodes []StatusCodeMatch `json:"statusCodes"` } // StatusCodeValueType defines the types of values for the status code match supported by Envoy Gateway. // +kubebuilder:validation:Enum=Value;Range type StatusCodeValueType string +const ( + // StatusCodeValueTypeValue defines the "Value" status code match type. + StatusCodeValueTypeValue StatusCodeValueType = "Value" + + // StatusCodeValueTypeRange defines the "Range" status code match type. + StatusCodeValueTypeRange StatusCodeValueType = "Range" +) + +// StatusCodeMatch defines the configuration for matching a status code. +// +kubebuilder:validation:XValidation:message="value must be set for type Value",rule="(!has(self.type) || self.type == 'Value')? has(self.value) : true" +// +kubebuilder:validation:XValidation:message="range must be set for type Range",rule="(has(self.type) && self.type == 'Range')? has(self.range) : true" type StatusCodeMatch struct { // Type is the type of value. + // Valid values are Value and Range, default is Value. // // +kubebuilder:default=Value + // +kubebuilder:validation:Enum=Value;Range // +unionDiscriminator Type *StatusCodeValueType `json:"type"` // Value contains the value of the status code. // // +optional - Value *string `json:"value,omitempty"` - // ValueRef contains the contents of the body - // specified as a local object reference. - // Only a reference to ConfigMap is supported. + Value *int `json:"value,omitempty"` + + // Range contains the range of status codes. // // +optional Range *StatusCodeRange `json:"range,omitempty"` } // StatusCodeRange defines the configuration for define a range of status codes. +// +kubebuilder:validation:XValidation: message="end must be greater than start",rule="self.end > self.start" type StatusCodeRange struct { // Start of the range, including the start value. Start int `json:"start"` @@ -669,19 +684,31 @@ type CustomResponse struct { ContentType *string `json:"contentType,omitempty"` // Body of the Custom Response - // - // +optional - Body *CustomResponseBody `json:"body,omitempty"` + Body CustomResponseBody `json:"body"` } // ResponseValueType defines the types of values for the response body supported by Envoy Gateway. // +kubebuilder:validation:Enum=Inline;ValueRef type ResponseValueType string +const ( + // ResponseValueTypeInline defines the "Inline" response body type. + ResponseValueTypeInline ResponseValueType = "Inline" + + // ResponseValueTypeValueRef defines the "ValueRef" response body type. + ResponseValueTypeValueRef ResponseValueType = "ValueRef" +) + // CustomResponseBody +// +kubebuilder:validation:XValidation:message="inline must be set for type Inline",rule="(!has(self.type) || self.type == 'Inline')? has(self.inline) : true" +// +kubebuilder:validation:XValidation:message="valueRef must be set for type ValueRef",rule="(has(self.type) && self.type == 'ValueRef')? has(self.valueRef) : true" +// +kubebuilder:validation:XValidation:message="only ConfigMap is supported for ValueRef",rule="has(self.valueRef) ? self.valueRef.kind == 'ConfigMap' : true" type CustomResponseBody struct { // Type is the type of method to use to read the body value. + // Valid values are Inline and ValueRef, default is Inline. // + // +kubebuilder:default=Inline + // +kubebuilder:validation:Enum=Inline;ValueRef // +unionDiscriminator Type *ResponseValueType `json:"type"` @@ -689,10 +716,14 @@ type CustomResponseBody struct { // // +optional Inline *string `json:"inline,omitempty"` + // ValueRef contains the contents of the body // specified as a local object reference. // Only a reference to ConfigMap is supported. // + // The value of key `response.body` in the ConfigMap will be used as the response body. + // If the key is not found, the first value in the ConfigMap will be used. + // // +optional ValueRef *gwapiv1.LocalObjectReference `json:"valueRef,omitempty"` } diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 696c99259fb..c225d65d39e 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -1148,11 +1148,7 @@ func (in *CustomResponse) DeepCopyInto(out *CustomResponse) { *out = new(string) **out = **in } - if in.Body != nil { - in, out := &in.Body, &out.Body - *out = new(CustomResponseBody) - (*in).DeepCopyInto(*out) - } + in.Body.DeepCopyInto(&out.Body) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResponse. @@ -1198,8 +1194,8 @@ func (in *CustomResponseBody) DeepCopy() *CustomResponseBody { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CustomResponseMatch) DeepCopyInto(out *CustomResponseMatch) { *out = *in - if in.StatusCode != nil { - in, out := &in.StatusCode, &out.StatusCode + if in.StatusCodes != nil { + in, out := &in.StatusCodes, &out.StatusCodes *out = make([]StatusCodeMatch, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) @@ -5166,7 +5162,7 @@ func (in *StatusCodeMatch) DeepCopyInto(out *StatusCodeMatch) { } if in.Value != nil { in, out := &in.Value, &out.Value - *out = new(string) + *out = new(int) **out = **in } if in.Range != nil { diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml index 7b2e937312d..f9fb0f329dd 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml @@ -940,16 +940,15 @@ spec: match: description: Match configuration. properties: - statusCode: + statusCodes: description: Status code to match on. The match evaluates to true if any of the matches are successful. items: + description: StatusCodeMatch defines the configuration + for matching a status code. properties: range: - description: |- - ValueRef contains the contents of the body - specified as a local object reference. - Only a reference to ConfigMap is supported. + description: Range contains the range of status codes. properties: end: description: End of the range, including the end @@ -963,23 +962,41 @@ spec: - end - start type: object + x-kubernetes-validations: + - message: end must be greater than start + rule: self.end > self.start type: + allOf: + - enum: + - Value + - Range + - enum: + - Value + - Range default: Value - description: Type is the type of value. - enum: - - Value - - Range + description: |- + Type is the type of value. + Valid values are Value and Range, default is Value. type: string value: description: Value contains the value of the status code. - type: string + type: integer required: - type type: object + x-kubernetes-validations: + - message: value must be set for type Value + rule: '(!has(self.type) || self.type == ''Value'')? + has(self.value) : true' + - message: range must be set for type Range + rule: '(has(self.type) && self.type == ''Range'')? has(self.range) + : true' + maxItems: 50 + minItems: 1 type: array required: - - statusCode + - statusCodes type: object response: description: Response configuration. @@ -992,17 +1009,26 @@ spec: string. type: string type: - description: Type is the type of method to use to read - the body value. - enum: - - Inline - - ValueRef + allOf: + - enum: + - Inline + - ValueRef + - enum: + - Inline + - ValueRef + default: Inline + description: |- + Type is the type of method to use to read the body value. + Valid values are Inline and ValueRef, default is Inline. type: string valueRef: description: |- ValueRef contains the contents of the body specified as a local object reference. Only a reference to ConfigMap is supported. + + The value of key `response.body` in the ConfigMap will be used as the response body. + If the key is not found, the first value in the ConfigMap will be used. properties: group: description: |- @@ -1031,10 +1057,22 @@ spec: required: - type type: object + x-kubernetes-validations: + - message: inline must be set for type Inline + rule: '(!has(self.type) || self.type == ''Inline'')? has(self.inline) + : true' + - message: valueRef must be set for type ValueRef + rule: '(has(self.type) && self.type == ''ValueRef'')? + has(self.valueRef) : true' + - message: only ConfigMap is supported for ValueRef + rule: 'has(self.valueRef) ? self.valueRef.kind == ''ConfigMap'' + : true' contentType: description: Content Type of the response. This will be set in the Content-Type header. type: string + required: + - body type: object required: - match diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml index 5b0130f2736..0733ed112b6 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_envoyproxies.yaml @@ -305,6 +305,8 @@ spec: - envoy.filters.http.ratelimit + - envoy.filters.http.custom_response + - envoy.filters.http.router Note: "envoy.filters.http.router" cannot be reordered, it's always the last filter in the chain. @@ -330,6 +332,7 @@ spec: - envoy.filters.http.rbac - envoy.filters.http.local_ratelimit - envoy.filters.http.ratelimit + - envoy.filters.http.custom_response type: string before: description: |- @@ -349,6 +352,7 @@ spec: - envoy.filters.http.rbac - envoy.filters.http.local_ratelimit - envoy.filters.http.ratelimit + - envoy.filters.http.custom_response type: string name: description: Name of the filter. @@ -366,6 +370,7 @@ spec: - envoy.filters.http.rbac - envoy.filters.http.local_ratelimit - envoy.filters.http.ratelimit + - envoy.filters.http.custom_response type: string required: - name diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml index 8a75fec4211..672cfb59df8 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml @@ -60,17 +60,26 @@ spec: description: Inline contains the value as an inline string. type: string type: - description: Type is the type of method to use to read the - body value. - enum: - - Inline - - ValueRef + allOf: + - enum: + - Inline + - ValueRef + - enum: + - Inline + - ValueRef + default: Inline + description: |- + Type is the type of method to use to read the body value. + Valid values are Inline and ValueRef, default is Inline. type: string valueRef: description: |- ValueRef contains the contents of the body specified as a local object reference. Only a reference to ConfigMap is supported. + + The value of key `response.body` in the ConfigMap will be used as the response body. + If the key is not found, the first value in the ConfigMap will be used. properties: group: description: |- @@ -99,6 +108,16 @@ spec: required: - type type: object + x-kubernetes-validations: + - message: inline must be set for type Inline + rule: '(!has(self.type) || self.type == ''Inline'')? has(self.inline) + : true' + - message: valueRef must be set for type ValueRef + rule: '(has(self.type) && self.type == ''ValueRef'')? has(self.valueRef) + : true' + - message: only ConfigMap is supported for ValueRef + rule: 'has(self.valueRef) ? self.valueRef.kind == ''ConfigMap'' + : true' contentType: description: Content Type of the response. This will be set in the Content-Type header. diff --git a/go.mod b/go.mod index 4090b698e5e..a10aa68bbac 100644 --- a/go.mod +++ b/go.mod @@ -281,7 +281,7 @@ require ( golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index b8f289a9df0..89b6804a2ba 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -10,9 +10,11 @@ import ( "fmt" "math" "sort" + "strconv" "strings" perr "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" @@ -34,6 +36,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(backendTrafficPolicies []*egv gateways []*GatewayContext, routes []RouteContext, xdsIR resource.XdsIRMap, + configMaps []*corev1.ConfigMap, ) []*egv1a1.BackendTrafficPolicy { res := []*egv1a1.BackendTrafficPolicy{} @@ -127,7 +130,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(backendTrafficPolicies []*egv } // Set conditions for translation error if it got any - if err := t.translateBackendTrafficPolicyForRoute(policy, route, xdsIR); err != nil { + if err := t.translateBackendTrafficPolicyForRoute(policy, route, xdsIR, configMaps); err != nil { status.SetTranslationErrorForPolicyAncestors(&policy.Status, ancestorRefs, t.GatewayControllerName, @@ -181,7 +184,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(backendTrafficPolicies []*egv } // Set conditions for translation error if it got any - if err := t.translateBackendTrafficPolicyForGateway(policy, currTarget, gateway, xdsIR); err != nil { + if err := t.translateBackendTrafficPolicyForGateway(policy, currTarget, gateway, xdsIR, configMaps); err != nil { status.SetTranslationErrorForPolicyAncestors(&policy.Status, ancestorRefs, t.GatewayControllerName, @@ -281,7 +284,12 @@ func resolveBTPolicyRouteTargetRef(policy *egv1a1.BackendTrafficPolicy, target g return route.RouteContext, nil } -func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.BackendTrafficPolicy, route RouteContext, xdsIR resource.XdsIRMap) error { +func (t *Translator) translateBackendTrafficPolicyForRoute( + policy *egv1a1.BackendTrafficPolicy, + route RouteContext, + xdsIR resource.XdsIRMap, + configMaps []*corev1.ConfigMap, +) error { var ( rl *ir.RateLimit lb *ir.LoadBalancer @@ -295,6 +303,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen bc *ir.BackendConnection ds *ir.DNS h2 *ir.HTTP2Settings + ro *ir.ResponseOverride err, errs error ) @@ -340,6 +349,11 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen errs = errors.Join(errs, err) } + if ro, err = buildResponseOverride(policy, configMaps); err != nil { + err = perr.WithMessage(err, "ResponseOverride") + errs = errors.Join(errs, err) + } + ds = translateDNS(policy.Spec.ClusterSettings) // Apply IR to all relevant routes @@ -402,6 +416,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen HTTP2: h2, DNS: ds, Timeout: to, + ResponseOverride: ro, } // Update the Host field in HealthCheck, now that we have access to the Route Hostname. @@ -418,7 +433,13 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen return errs } -func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.BackendTrafficPolicy, target gwapiv1a2.LocalPolicyTargetReferenceWithSectionName, gateway *GatewayContext, xdsIR resource.XdsIRMap) error { +func (t *Translator) translateBackendTrafficPolicyForGateway( + policy *egv1a1.BackendTrafficPolicy, + target gwapiv1a2.LocalPolicyTargetReferenceWithSectionName, + gateway *GatewayContext, + xdsIR resource.XdsIRMap, + configMaps []*corev1.ConfigMap, +) error { var ( rl *ir.RateLimit lb *ir.LoadBalancer @@ -431,6 +452,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back rt *ir.Retry ds *ir.DNS h2 *ir.HTTP2Settings + ro *ir.ResponseOverride err, errs error ) @@ -469,6 +491,10 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back err = perr.WithMessage(err, "HTTP2") errs = errors.Join(errs, err) } + if ro, err = buildResponseOverride(policy, configMaps); err != nil { + err = perr.WithMessage(err, "ResponseOverride") + errs = errors.Join(errs, err) + } ds = translateDNS(policy.Spec.ClusterSettings) @@ -542,16 +568,17 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back } r.Traffic = &ir.TrafficFeatures{ - RateLimit: rl, - LoadBalancer: lb, - ProxyProtocol: pp, - HealthCheck: hc, - CircuitBreaker: cb, - FaultInjection: fi, - TCPKeepalive: ka, - Retry: rt, - HTTP2: h2, - DNS: ds, + RateLimit: rl, + LoadBalancer: lb, + ProxyProtocol: pp, + HealthCheck: hc, + CircuitBreaker: cb, + FaultInjection: fi, + TCPKeepalive: ka, + Retry: rt, + HTTP2: h2, + DNS: ds, + ResponseOverride: ro, } // Update the Host field in HealthCheck, now that we have access to the Route Hostname. @@ -836,3 +863,81 @@ func makeIrTriggerSet(in []egv1a1.TriggerEnum) []ir.TriggerEnum { } return irTriggers } + +func buildResponseOverride(policy *egv1a1.BackendTrafficPolicy, configMaps []*corev1.ConfigMap) (*ir.ResponseOverride, error) { + if len(policy.Spec.ResponseOverride) == 0 { + return nil, nil + } + + rules := make([]ir.ResponseOverrideRule, 0, len(policy.Spec.ResponseOverride)) + for index, ro := range policy.Spec.ResponseOverride { + match := ir.CustomResponseMatch{ + StatusCodes: make([]ir.StatusCodeMatch, 0, len(ro.Match.StatusCodes)), + } + + for _, code := range ro.Match.StatusCodes { + if code.Type != nil && *code.Type == egv1a1.StatusCodeValueTypeRange { + match.StatusCodes = append(match.StatusCodes, ir.StatusCodeMatch{ + Range: &ir.StatusCodeRange{ + Start: code.Range.Start, + End: code.Range.End, + }, + }) + } else { + match.StatusCodes = append(match.StatusCodes, ir.StatusCodeMatch{ + Value: code.Value, + }) + } + } + + response := ir.CustomResponse{ + ContentType: ro.Response.ContentType, + } + + if ro.Response.Body.Type != nil && *ro.Response.Body.Type == egv1a1.ResponseValueTypeValueRef { + foundCM := false + for _, cm := range configMaps { + if cm.Namespace == policy.Namespace && cm.Name == string(ro.Response.Body.ValueRef.Name) { + body, dataOk := cm.Data["response.body"] + switch { + case dataOk: + response.Body = body + case len(cm.Data) > 0: // Fallback to the first key if response.body is not found + for _, value := range cm.Data { + body = value + break + } + response.Body = body + default: + return nil, fmt.Errorf("can't find the key response.body in the referenced configmap %s", ro.Response.Body.ValueRef.Name) + } + + foundCM = true + break + } + } + if !foundCM { + return nil, fmt.Errorf("can't find the referenced configmap %s", ro.Response.Body.ValueRef.Name) + } + } else { + response.Body = *ro.Response.Body.Inline + } + + rules = append(rules, ir.ResponseOverrideRule{ + Name: defaultResponseOverrideRuleName(policy, index), + Match: match, + Response: response, + }) + } + return &ir.ResponseOverride{ + Name: irConfigName(policy), + Rules: rules, + }, nil +} + +func defaultResponseOverrideRuleName(policy *egv1a1.BackendTrafficPolicy, index int) string { + return fmt.Sprintf( + "%s/responseoverride/rule/%s", + irConfigName(policy), + strconv.Itoa(index)) +} diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override-invalid-valueref.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override-invalid-valueref.in.yaml new file mode 100644 index 00000000000..e44a8473d5c --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override-invalid-valueref.in.yaml @@ -0,0 +1,141 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: default + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +configMaps: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: response-override-config + namespace: default + data: {} +backendTrafficPolicies: + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + responseOverride: + - match: + statusCodes: + - type: Value + value: 404 + response: + contentType: text/plain + body: + type: Inline + inline: "gateway-1 Not Found" + - match: + statusCodes: + - type: Value + value: 500 + - type: Range + range: + start: 501 + end: 511 + response: + contentType: application/json + body: + type: ValueRef + valueRef: + group: "" + kind: ConfigMap + name: response-override-config + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + responseOverride: + - match: + statusCodes: + - value: 404 + response: + contentType: text/plain + body: + inline: "httproute-1 Not Found" + - match: + statusCodes: + - value: 500 + - type: Range + range: + start: 501 + end: 511 + response: + contentType: application/json + body: + type: ValueRef + valueRef: + group: "" + kind: ConfigMap + name: response-override-config-1 diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override-invalid-valueref.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override-invalid-valueref.out.yaml new file mode 100644 index 00000000000..c1542d9caec --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override-invalid-valueref.out.yaml @@ -0,0 +1,371 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + responseOverride: + - match: + statusCodes: + - type: null + value: 404 + response: + body: + inline: httproute-1 Not Found + type: null + contentType: text/plain + - match: + statusCodes: + - type: null + value: 500 + - range: + end: 511 + start: 501 + type: Range + response: + body: + type: ValueRef + valueRef: + group: "" + kind: ConfigMap + name: response-override-config-1 + contentType: application/json + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: default + sectionName: http + conditions: + - lastTransitionTime: null + message: 'ResponseOverride: can''t find the referenced configmap response-override-config-1.' + reason: Invalid + status: "False" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: default + spec: + responseOverride: + - match: + statusCodes: + - type: Value + value: 404 + response: + body: + inline: gateway-1 Not Found + type: Inline + contentType: text/plain + - match: + statusCodes: + - type: Value + value: 500 + - range: + end: 511 + start: 501 + type: Range + response: + body: + type: ValueRef + valueRef: + group: "" + kind: ConfigMap + name: response-override-config + contentType: application/json + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + conditions: + - lastTransitionTime: null + message: 'ResponseOverride: can''t find the key response.body in the referenced + configmap response-override-config.' + reason: Invalid + status: "False" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-2 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: default + sectionName: http +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: default + name: default/gateway-1 + default/gateway-2: + proxy: + listeners: + - address: null + name: default/gateway-2/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: default + name: default/gateway-2 +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + metadata: + kind: Gateway + name: gateway-1 + namespace: default + sectionName: http + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + directResponse: + statusCode: 500 + hostname: '*' + isHTTP2: true + metadata: + kind: GRPCRoute + name: grpcroute-1 + namespace: default + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + default/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-2 + namespace: default + sectionName: http + name: default/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + directResponse: + statusCode: 500 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.in.yaml new file mode 100644 index 00000000000..51dd9fd7114 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.in.yaml @@ -0,0 +1,145 @@ +gateways: + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All + - apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: gateway-2 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: All +grpcRoutes: + - apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + namespace: default + name: grpcroute-1 + spec: + parentRefs: + - namespace: default + name: gateway-1 + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 +httpRoutes: + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - namespace: default + name: gateway-2 + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 +configMaps: + - apiVersion: v1 + kind: ConfigMap + metadata: + name: response-override-config + namespace: default + data: + response.body: | + { + "error": "Internal Server Error" + } +backendTrafficPolicies: + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-gateway + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + responseOverride: + - match: + statusCodes: + - type: Value + value: 404 + response: + contentType: text/plain + body: + type: Inline + inline: "gateway-1 Not Found" + - match: + statusCodes: + - type: Value + value: 500 + - type: Range + range: + start: 501 + end: 511 + response: + contentType: application/json + body: + type: ValueRef + valueRef: + group: "" + kind: ConfigMap + name: response-override-config + - apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + namespace: default + name: policy-for-route + spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + responseOverride: + - match: + statusCodes: + - value: 404 + response: + contentType: text/plain + body: + inline: "httproute-1 Not Found" + - match: + statusCodes: + - value: 500 + - type: Range + range: + start: 501 + end: 511 + response: + contentType: application/json + body: + type: ValueRef + valueRef: + group: "" + kind: ConfigMap + name: response-override-config diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml new file mode 100644 index 00000000000..568a57af484 --- /dev/null +++ b/internal/gatewayapi/testdata/backendtrafficpolicy-with-response-override.out.yaml @@ -0,0 +1,414 @@ +backendTrafficPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-route + namespace: default + spec: + responseOverride: + - match: + statusCodes: + - type: null + value: 404 + response: + body: + inline: httproute-1 Not Found + type: null + contentType: text/plain + - match: + statusCodes: + - type: null + value: 500 + - range: + end: 511 + start: 501 + type: Range + response: + body: + type: ValueRef + valueRef: + group: "" + kind: ConfigMap + name: response-override-config + contentType: application/json + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: httproute-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-2 + namespace: default + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: BackendTrafficPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway + namespace: default + spec: + responseOverride: + - match: + statusCodes: + - type: Value + value: 404 + response: + body: + inline: gateway-1 Not Found + type: Inline + contentType: text/plain + - match: + statusCodes: + - type: Value + value: 500 + - range: + end: 511 + start: 501 + type: Range + response: + body: + type: ValueRef + valueRef: + group: "" + kind: ConfigMap + name: response-override-config + contentType: application/json + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: default + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-2 + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +grpcRoutes: +- apiVersion: gateway.networking.k8s.io/v1alpha2 + kind: GRPCRoute + metadata: + creationTimestamp: null + name: grpcroute-1 + namespace: default + spec: + parentRefs: + - name: gateway-1 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-1 + namespace: default + sectionName: http +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - gateway.envoyproxy.io + parentRefs: + - name: gateway-2 + namespace: default + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: gateway-2 + namespace: default + sectionName: http +infraIR: + default/gateway-1: + proxy: + listeners: + - address: null + name: default/gateway-1/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: default + name: default/gateway-1 + default/gateway-2: + proxy: + listeners: + - address: null + name: default/gateway-2/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-2 + gateway.envoyproxy.io/owning-gateway-namespace: default + name: default/gateway-2 +xdsIR: + default/gateway-1: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: true + metadata: + kind: Gateway + name: gateway-1 + namespace: default + sectionName: http + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: grpcroute/default/grpcroute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: GRPC + weight: 1 + hostname: '*' + isHTTP2: true + metadata: + kind: GRPCRoute + name: grpcroute-1 + namespace: default + name: grpcroute/default/grpcroute-1/rule/0/match/-1/* + traffic: + responseOverride: + name: backendtrafficpolicy/default/policy-for-gateway + rules: + - match: + statusCodes: + - value: 404 + name: backendtrafficpolicy/default/policy-for-gateway/responseoverride/rule/0 + response: + body: gateway-1 Not Found + contentType: text/plain + - match: + statusCodes: + - value: 500 + - range: + end: 511 + start: 501 + name: backendtrafficpolicy/default/policy-for-gateway/responseoverride/rule/1 + response: + body: | + { + "error": "Internal Server Error" + } + contentType: application/json + default/gateway-2: + accessLog: + text: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: gateway-2 + namespace: default + sectionName: http + name: default/gateway-2/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: gateway.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: / + traffic: + responseOverride: + name: backendtrafficpolicy/default/policy-for-route + rules: + - match: + statusCodes: + - value: 404 + name: backendtrafficpolicy/default/policy-for-route/responseoverride/rule/0 + response: + body: httproute-1 Not Found + contentType: text/plain + - match: + statusCodes: + - value: 500 + - range: + end: 511 + start: 501 + name: backendtrafficpolicy/default/policy-for-route/responseoverride/rule/1 + response: + body: | + { + "error": "Internal Server Error" + } + contentType: application/json diff --git a/internal/gatewayapi/translator.go b/internal/gatewayapi/translator.go index 0e6d683d855..0f518b71033 100644 --- a/internal/gatewayapi/translator.go +++ b/internal/gatewayapi/translator.go @@ -211,7 +211,7 @@ func (t *Translator) Translate(resources *resource.Resources) (*TranslateResult, // Process BackendTrafficPolicies backendTrafficPolicies := t.ProcessBackendTrafficPolicies( - resources.BackendTrafficPolicies, gateways, routes, xdsIR) + resources.BackendTrafficPolicies, gateways, routes, xdsIR, resources.ConfigMaps) // Process SecurityPolicies securityPolicies := t.ProcessSecurityPolicies( diff --git a/internal/ir/xds.go b/internal/ir/xds.go index fdcace324f5..cb5021f4c9f 100644 --- a/internal/ir/xds.go +++ b/internal/ir/xds.go @@ -477,6 +477,63 @@ type HTTP2Settings struct { ResetStreamOnError *bool `json:"resetStreamOnError,omitempty" yaml:"resetStreamOnError,omitempty"` } +// ResponseOverride defines the configuration to override specific responses with a custom one. +// +k8s:deepcopy-gen=true +type ResponseOverride struct { + // Name is a unique name for a ResponseOverride configuration. + // The xds translator only generates one CustomResponse filter for each unique name. + Name string `json:"name" yaml:"name"` + + // Rules contains the list of rules to override responses. + Rules []ResponseOverrideRule `json:"rules,omitempty"` +} + +// ResponseOverrideRule defines the configuration for overriding a response. +// +k8s:deepcopy-gen=true +type ResponseOverrideRule struct { + // Name is a generated name for the rule. + Name string `json:"name"` + // Match configuration. + Match CustomResponseMatch `json:"match"` + // Response configuration. + Response CustomResponse `json:"response"` +} + +// CustomResponseMatch defines the configuration for matching a user response to return a custom one. +// +k8s:deepcopy-gen=true +type CustomResponseMatch struct { + // Status code to match on. The match evaluates to true if any of the matches are successful. + StatusCodes []StatusCodeMatch `json:"statusCodes"` +} + +// StatusCodeMatch defines the configuration for matching a status code. +// +k8s:deepcopy-gen=true +type StatusCodeMatch struct { + // Value contains the value of the status code. + Value *int `json:"value,omitempty"` + + // Range contains a range of status codes. + Range *StatusCodeRange `json:"range,omitempty"` +} + +// StatusCodeRange defines the configuration for define a range of status codes. +type StatusCodeRange struct { + // Start of the range, including the start value. + Start int `json:"start"` + // End of the range, including the end value. + End int `json:"end"` +} + +// CustomResponse defines the configuration for returning a custom response. +// +k8s:deepcopy-gen=true +type CustomResponse struct { + // Content Type of the response. This will be set in the Content-Type header. + ContentType *string `json:"contentType,omitempty"` + + // Body of the Custom Response + Body string `json:"body"` +} + // HealthCheckSettings provides HealthCheck configuration on the HTTP/HTTPS listener. // +k8s:deepcopy-gen=true type HealthCheckSettings egv1a1.HealthCheckSettings @@ -657,6 +714,8 @@ type TrafficFeatures struct { HTTP2 *HTTP2Settings `json:"http2,omitempty" yaml:"http2,omitempty"` // DNS is used to configure how DNS resolution is handled by the Envoy Proxy cluster DNS *DNS `json:"dns,omitempty" yaml:"dns,omitempty"` + // ResponseOverride defines the schema for overriding the response. + ResponseOverride *ResponseOverride `json:"responseOverride,omitempty" yaml:"responseOverride,omitempty"` } func (b *TrafficFeatures) Validate() error { diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go index 3c0c1135f44..791b6d5dd68 100644 --- a/internal/ir/zz_generated.deepcopy.go +++ b/internal/ir/zz_generated.deepcopy.go @@ -602,6 +602,48 @@ func (in *CoreListenerDetails) DeepCopy() *CoreListenerDetails { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CustomResponse) DeepCopyInto(out *CustomResponse) { + *out = *in + if in.ContentType != nil { + in, out := &in.ContentType, &out.ContentType + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResponse. +func (in *CustomResponse) DeepCopy() *CustomResponse { + if in == nil { + return nil + } + out := new(CustomResponse) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CustomResponseMatch) DeepCopyInto(out *CustomResponseMatch) { + *out = *in + if in.StatusCodes != nil { + in, out := &in.StatusCodes, &out.StatusCodes + *out = make([]StatusCodeMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CustomResponseMatch. +func (in *CustomResponseMatch) DeepCopy() *CustomResponseMatch { + if in == nil { + return nil + } + out := new(CustomResponseMatch) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DNS) DeepCopyInto(out *DNS) { *out = *in @@ -2399,6 +2441,45 @@ func (in *ResourceMetadata) DeepCopy() *ResourceMetadata { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResponseOverride) DeepCopyInto(out *ResponseOverride) { + *out = *in + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]ResponseOverrideRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResponseOverride. +func (in *ResponseOverride) DeepCopy() *ResponseOverride { + if in == nil { + return nil + } + out := new(ResponseOverride) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ResponseOverrideRule) DeepCopyInto(out *ResponseOverrideRule) { + *out = *in + in.Match.DeepCopyInto(&out.Match) + in.Response.DeepCopyInto(&out.Response) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResponseOverrideRule. +func (in *ResponseOverrideRule) DeepCopy() *ResponseOverrideRule { + if in == nil { + return nil + } + out := new(ResponseOverrideRule) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Retry) DeepCopyInto(out *Retry) { *out = *in @@ -2590,6 +2671,31 @@ func (in *SlowStart) DeepCopy() *SlowStart { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StatusCodeMatch) DeepCopyInto(out *StatusCodeMatch) { + *out = *in + if in.Value != nil { + in, out := &in.Value, &out.Value + *out = new(int) + **out = **in + } + if in.Range != nil { + in, out := &in.Range, &out.Range + *out = new(StatusCodeRange) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StatusCodeMatch. +func (in *StatusCodeMatch) DeepCopy() *StatusCodeMatch { + if in == nil { + return nil + } + out := new(StatusCodeMatch) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StringMatch) DeepCopyInto(out *StringMatch) { *out = *in @@ -3159,6 +3265,11 @@ func (in *TrafficFeatures) DeepCopyInto(out *TrafficFeatures) { *out = new(DNS) (*in).DeepCopyInto(*out) } + if in.ResponseOverride != nil { + in, out := &in.ResponseOverride, &out.ResponseOverride + *out = new(ResponseOverride) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficFeatures. diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 7fe3c3d32ff..de020dfcee8 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -239,7 +239,7 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, _ reconcile.Reques } // Add all BackendTrafficPolicies to the resourceTree - if err = r.processBackendTrafficPolicies(ctx, gwcResource); err != nil { + if err = r.processBackendTrafficPolicies(ctx, gwcResource, resourceMappings); err != nil { return reconcile.Result{}, err } @@ -748,6 +748,39 @@ func (r *gatewayAPIReconciler) processConfigMapRef( return nil } +// processBtpConfigMapRefs adds the referenced ConfigMaps in BackendTrafficPolicies +// to the resourceTree +func (r *gatewayAPIReconciler) processBtpConfigMapRefs( + ctx context.Context, resourceTree *resource.Resources, resourceMap *resourceMappings, +) { + for _, policy := range resourceTree.BackendTrafficPolicies { + for _, ro := range policy.Spec.ResponseOverride { + if ro.Response.Body.ValueRef != nil && string(ro.Response.Body.ValueRef.Kind) == resource.KindConfigMap { + configMap := new(corev1.ConfigMap) + err := r.client.Get(ctx, + types.NamespacedName{Namespace: policy.Namespace, Name: string(ro.Response.Body.ValueRef.Name)}, + configMap, + ) + // we don't return an error here, because we want to continue + // reconciling the rest of the BackendTrafficPolicies despite that this + // reference is invalid. + // This BackendTrafficPolicies will be marked as invalid in its status + // when translating to IR because the referenced configmap can't be + // found. + if err != nil { + r.log.Error(err, + "failed to process ResponseOverride ValueRef for BackendTrafficPolicy", + "policy", policy, "ValueRef", ro.Response.Body.ValueRef.Name) + } + + resourceMap.allAssociatedNamespaces.Insert(policy.Namespace) + resourceTree.ConfigMaps = append(resourceTree.ConfigMaps, configMap) + r.log.Info("processing ConfigMap", "namespace", policy.Namespace, "name", string(ro.Response.Body.ValueRef.Name)) + } + } + } +} + func (r *gatewayAPIReconciler) getNamespace(ctx context.Context, name string) (*corev1.Namespace, error) { nsKey := types.NamespacedName{Name: name} ns := new(corev1.Namespace) @@ -942,7 +975,8 @@ func (r *gatewayAPIReconciler) processClientTrafficPolicies( } // processBackendTrafficPolicies adds BackendTrafficPolicies to the resourceTree -func (r *gatewayAPIReconciler) processBackendTrafficPolicies(ctx context.Context, resourceTree *resource.Resources) error { +func (r *gatewayAPIReconciler) processBackendTrafficPolicies(ctx context.Context, resourceTree *resource.Resources, resourceMap *resourceMappings, +) error { backendTrafficPolicies := egv1a1.BackendTrafficPolicyList{} if err := r.client.List(ctx, &backendTrafficPolicies); err != nil { return fmt.Errorf("error listing BackendTrafficPolicies: %w", err) @@ -955,6 +989,7 @@ func (r *gatewayAPIReconciler) processBackendTrafficPolicies(ctx context.Context policy.Status = gwapiv1a2.PolicyStatus{} resourceTree.BackendTrafficPolicies = append(resourceTree.BackendTrafficPolicies, &policy) } + r.processBtpConfigMapRefs(ctx, resourceTree, resourceMap) return nil } @@ -1348,7 +1383,7 @@ func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.M return err } - // Watch ConfigMap CRUDs and process affected ClienTraffiPolicies and BackendTLSPolicies. + // Watch ConfigMap CRUDs and process affected EG Resources. configMapPredicates := []predicate.TypedPredicate[*corev1.ConfigMap]{ predicate.NewTypedPredicateFuncs[*corev1.ConfigMap](func(cm *corev1.ConfigMap) bool { return r.validateConfigMapForReconcile(cm) @@ -1492,6 +1527,10 @@ func (r *gatewayAPIReconciler) watchResources(ctx context.Context, mgr manager.M return err } + if err := addBtpIndexers(ctx, mgr); err != nil { + return err + } + // Watch SecurityPolicy spPredicates := []predicate.TypedPredicate[*egv1a1.SecurityPolicy]{ predicate.TypedGenerationChangedPredicate[*egv1a1.SecurityPolicy]{}, diff --git a/internal/provider/kubernetes/indexers.go b/internal/provider/kubernetes/indexers.go index 462a70542f3..2ad12069f98 100644 --- a/internal/provider/kubernetes/indexers.go +++ b/internal/provider/kubernetes/indexers.go @@ -46,6 +46,7 @@ const ( secretEnvoyProxyIndex = "secretEnvoyProxyIndex" secretEnvoyExtensionPolicyIndex = "secretEnvoyExtensionPolicyIndex" httpRouteFilterHTTPRouteIndex = "httpRouteFilterHTTPRouteIndex" + configMapBtpIndex = "configMapBtpIndex" ) func addReferenceGrantIndexers(ctx context.Context, mgr manager.Manager) error { @@ -641,6 +642,36 @@ func secretCtpIndexFunc(rawObj client.Object) []string { return secretReferences } +// addBtpIndexers adds indexing on BackendTrafficPolicy, for ConfigMap objects that are +// referenced in BackendTrafficPolicy objects. This helps in querying for BackendTrafficPolies that are +// affected by a particular ConfigMap CRUD. +func addBtpIndexers(ctx context.Context, mgr manager.Manager) error { + if err := mgr.GetFieldIndexer().IndexField(ctx, &egv1a1.BackendTrafficPolicy{}, configMapBtpIndex, configMapBtpIndexFunc); err != nil { + return err + } + + return nil +} + +func configMapBtpIndexFunc(rawObj client.Object) []string { + btp := rawObj.(*egv1a1.BackendTrafficPolicy) + var configMapReferences []string + + for _, ro := range btp.Spec.ResponseOverride { + if ro.Response.Body.ValueRef != nil { + if string(ro.Response.Body.ValueRef.Kind) == resource.KindConfigMap { + configMapReferences = append(configMapReferences, + types.NamespacedName{ + Namespace: btp.Namespace, + Name: string(ro.Response.Body.ValueRef.Name), + }.String(), + ) + } + } + } + return configMapReferences +} + // addBtlsIndexers adds indexing on BackendTLSPolicy, for ConfigMap objects that are // referenced in BackendTLSPolicy objects. This helps in querying for BackendTLSPolicies that are // affected by a particular ConfigMap CRUD. diff --git a/internal/provider/kubernetes/predicates.go b/internal/provider/kubernetes/predicates.go index 9c4d582b58b..a885d58ca62 100644 --- a/internal/provider/kubernetes/predicates.go +++ b/internal/provider/kubernetes/predicates.go @@ -588,7 +588,7 @@ func (r *gatewayAPIReconciler) handleNode(obj client.Object) bool { return true } -// validateConfigMapForReconcile checks whether the ConfigMap belongs to a valid ClientTrafficPolicy. +// validateConfigMapForReconcile checks whether the ConfigMap belongs to a valid EG resource. func (r *gatewayAPIReconciler) validateConfigMapForReconcile(obj client.Object) bool { configMap, ok := obj.(*corev1.ConfigMap) if !ok { @@ -604,8 +604,8 @@ func (r *gatewayAPIReconciler) validateConfigMapForReconcile(obj client.Object) return false } - if len(ctpList.Items) == 0 { - return false + if len(ctpList.Items) > 0 { + return true } btlsList := &gwapiv1a3.BackendTLSPolicyList{} @@ -616,11 +616,23 @@ func (r *gatewayAPIReconciler) validateConfigMapForReconcile(obj client.Object) return false } - if len(btlsList.Items) == 0 { + if len(btlsList.Items) > 0 { + return true + } + + btpList := &egv1a1.BackendTrafficPolicyList{} + if err := r.client.List(context.Background(), btpList, &client.ListOptions{ + FieldSelector: fields.OneTermEqualSelector(configMapBtpIndex, utils.NamespacedName(configMap).String()), + }); err != nil { + r.log.Error(err, "unable to find associated BackendTrafficPolicy") return false } - return true + if len(btpList.Items) > 0 { + return true + } + + return false } func (r *gatewayAPIReconciler) isEnvoyExtensionPolicyReferencingBackend(nsName *types.NamespacedName) bool { diff --git a/internal/xds/translator/custom_response.go b/internal/xds/translator/custom_response.go new file mode 100644 index 00000000000..1d1bf3a5d2c --- /dev/null +++ b/internal/xds/translator/custom_response.go @@ -0,0 +1,450 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package translator + +import ( + "errors" + "fmt" + "strconv" + + cncfv3 "github.com/cncf/xds/go/xds/core/v3" + matcherv3 "github.com/cncf/xds/go/xds/type/matcher/v3" + typev3 "github.com/cncf/xds/go/xds/type/v3" + corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" + respv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/custom_response/v3" + hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + policyv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/http/custom_response/local_response_policy/v3" + envoymatcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" + expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1" + "google.golang.org/protobuf/types/known/anypb" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/xds/types" +) + +func init() { + registerHTTPFilter(&customResponse{}) +} + +type customResponse struct{} + +var _ httpFilter = &customResponse{} + +// patchHCM builds and appends the customResponse Filters to the HTTP Connection Manager +// if applicable, and it does not already exist. +// Note: this method creates an customResponse filter for each route that contains an ResponseOverride config. +// the filter is disabled by default. It is enabled on the route level. +func (c *customResponse) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error { + var errs error + + if mgr == nil { + return errors.New("hcm is nil") + } + + if irListener == nil { + return errors.New("ir listener is nil") + } + + for _, route := range irListener.Routes { + if !c.routeContainsResponseOverride(route) { + continue + } + + // Only generates one CustomResponse Envoy filter for each unique name. + // For example, if there are two routes under the same gateway with the + // same CustomResponse config, only one CustomResponse filter will be generated. + if hcmContainsFilter(mgr, c.customResponseFilterName(route.Traffic.ResponseOverride)) { + continue + } + + filter, err := c.buildHCMCustomResponseFilter(route.Traffic.ResponseOverride) + if err != nil { + errs = errors.Join(errs, err) + continue + } + + mgr.HttpFilters = append(mgr.HttpFilters, filter) + } + + return errs +} + +// buildHCMCustomResponseFilter returns an OAuth2 HTTP filter from the provided IR HTTPRoute. +func (c *customResponse) buildHCMCustomResponseFilter(ro *ir.ResponseOverride) (*hcmv3.HttpFilter, error) { + proto, err := c.customResponseConfig(ro) + if err != nil { + return nil, err + } + + if err := proto.ValidateAll(); err != nil { + return nil, err + } + + any, err := anypb.New(proto) + if err != nil { + return nil, err + } + + return &hcmv3.HttpFilter{ + Name: c.customResponseFilterName(ro), + Disabled: true, + ConfigType: &hcmv3.HttpFilter_TypedConfig{ + TypedConfig: any, + }, + }, nil +} + +func (c *customResponse) customResponseFilterName(ro *ir.ResponseOverride) string { + return perRouteFilterName(egv1a1.EnvoyFilterCustomResponse, ro.Name) +} + +func (c *customResponse) customResponseConfig(ro *ir.ResponseOverride) (*respv3.CustomResponse, error) { + var matchers []*matcherv3.Matcher_MatcherList_FieldMatcher + + for _, r := range ro.Rules { + var ( + action *matcherv3.Matcher_OnMatch_Action + predicate *matcherv3.Matcher_MatcherList_Predicate + err error + ) + + if action, err = c.buildAction(r); err != nil { + return nil, err + } + + switch { + case len(r.Match.StatusCodes) == 0: + // This is just a sanity check, as the CRD validation should have caught this. + return nil, fmt.Errorf("missing status code in response override rule") + case len(r.Match.StatusCodes) == 1: + if predicate, err = c.buildSinglePredicate(r.Match.StatusCodes[0]); err != nil { + return nil, err + } + + matcher := &matcherv3.Matcher_MatcherList_FieldMatcher{ + Predicate: predicate, + OnMatch: &matcherv3.Matcher_OnMatch{ + OnMatch: action, + }, + } + + matchers = append(matchers, matcher) + case len(r.Match.StatusCodes) > 1: + var predicates []*matcherv3.Matcher_MatcherList_Predicate + + for _, codeMatch := range r.Match.StatusCodes { + if predicate, err = c.buildSinglePredicate(codeMatch); err != nil { + return nil, err + } + + predicates = append(predicates, predicate) + } + + // Create a single matcher that ORs all the predicates together. + // The rule will match if any of the codes match. + matcher := &matcherv3.Matcher_MatcherList_FieldMatcher{ + Predicate: &matcherv3.Matcher_MatcherList_Predicate{ + MatchType: &matcherv3.Matcher_MatcherList_Predicate_OrMatcher{ + OrMatcher: &matcherv3.Matcher_MatcherList_Predicate_PredicateList{ + Predicate: predicates, + }, + }, + }, + OnMatch: &matcherv3.Matcher_OnMatch{ + OnMatch: action, + }, + } + + matchers = append(matchers, matcher) + } + + } + + // Create a MatcherList. + // The rules will be evaluated in order, and the first match wins. + cr := &respv3.CustomResponse{ + CustomResponseMatcher: &matcherv3.Matcher{ + MatcherType: &matcherv3.Matcher_MatcherList_{ + MatcherList: &matcherv3.Matcher_MatcherList{ + Matchers: matchers, + }, + }, + }, + } + + return cr, nil +} + +func (c *customResponse) buildSinglePredicate(codeMatch ir.StatusCodeMatch) (*matcherv3.Matcher_MatcherList_Predicate, error) { + var ( + httpAttributeCELInput *cncfv3.TypedExtensionConfig + statusCodeInput *cncfv3.TypedExtensionConfig + statusCodeCELMatcher *cncfv3.TypedExtensionConfig + err error + ) + + // Use CEL to match a range of status codes. + if codeMatch.Range != nil { + if httpAttributeCELInput, err = c.buildHTTPAttributeCELInput(); err != nil { + return nil, err + } + + if statusCodeCELMatcher, err = c.buildStatusCodeCELMatcher(*codeMatch.Range); err != nil { + return nil, err + } + + return &matcherv3.Matcher_MatcherList_Predicate{ + MatchType: &matcherv3.Matcher_MatcherList_Predicate_SinglePredicate_{ + SinglePredicate: &matcherv3.Matcher_MatcherList_Predicate_SinglePredicate{ + Input: httpAttributeCELInput, + Matcher: &matcherv3.Matcher_MatcherList_Predicate_SinglePredicate_CustomMatch{ + CustomMatch: statusCodeCELMatcher, + }, + }, + }, + }, nil + } else { + // Use exact string match to match a single status code. + if statusCodeInput, err = c.buildStatusCodeInput(); err != nil { + return nil, err + } + + return &matcherv3.Matcher_MatcherList_Predicate{ + MatchType: &matcherv3.Matcher_MatcherList_Predicate_SinglePredicate_{ + SinglePredicate: &matcherv3.Matcher_MatcherList_Predicate_SinglePredicate{ + Input: statusCodeInput, + Matcher: &matcherv3.Matcher_MatcherList_Predicate_SinglePredicate_ValueMatch{ + ValueMatch: &matcherv3.StringMatcher{ + MatchPattern: &matcherv3.StringMatcher_Exact{ + Exact: strconv.Itoa(*codeMatch.Value), + }, + }, + }, + }, + }, + }, nil + } +} + +func (c *customResponse) buildHTTPAttributeCELInput() (*cncfv3.TypedExtensionConfig, error) { + var ( + pb *anypb.Any + err error + ) + + if pb, err = anypb.New(&matcherv3.HttpAttributesCelMatchInput{}); err != nil { + return nil, err + } + + return &cncfv3.TypedExtensionConfig{ + Name: "http-attributes-cel-match-input", + TypedConfig: pb, + }, nil +} + +func (c *customResponse) buildStatusCodeInput() (*cncfv3.TypedExtensionConfig, error) { + var ( + pb *anypb.Any + err error + ) + + if pb, err = anypb.New(&envoymatcherv3.HttpResponseStatusCodeMatchInput{}); err != nil { + return nil, err + } + + return &cncfv3.TypedExtensionConfig{ + Name: "http-response-status-code-match-input", + TypedConfig: pb, + }, nil +} + +func (c *customResponse) buildStatusCodeCELMatcher(codeRange ir.StatusCodeRange) (*cncfv3.TypedExtensionConfig, error) { + var ( + pb *anypb.Any + err error + ) + + // Build the CEL expression AST: response.code >= codeRange.Start && response.code <= codeRange.End + matcher := &matcherv3.CelMatcher{ + ExprMatch: &typev3.CelExpression{ + ExprSpecifier: &typev3.CelExpression_ParsedExpr{ + ParsedExpr: &expr.ParsedExpr{ + Expr: &expr.Expr{ + Id: 9, + ExprKind: &expr.Expr_CallExpr{ + CallExpr: &expr.Expr_Call{ + Function: "_&&_", + Args: []*expr.Expr{ + { + Id: 3, + ExprKind: &expr.Expr_CallExpr{ + CallExpr: &expr.Expr_Call{ + Function: "_>=_", + Args: []*expr.Expr{ + { + Id: 2, + ExprKind: &expr.Expr_SelectExpr{ + SelectExpr: &expr.Expr_Select{ + Operand: &expr.Expr{ + Id: 1, + ExprKind: &expr.Expr_IdentExpr{ + IdentExpr: &expr.Expr_Ident{ + Name: "response", + }, + }, + }, + Field: "code", + }, + }, + }, + { + Id: 4, + ExprKind: &expr.Expr_ConstExpr{ + ConstExpr: &expr.Constant{ + ConstantKind: &expr.Constant_Int64Value{ + Int64Value: int64(codeRange.Start), + }, + }, + }, + }, + }, + }, + }, + }, + { + Id: 7, + ExprKind: &expr.Expr_CallExpr{ + CallExpr: &expr.Expr_Call{ + Function: "_<=_", + Args: []*expr.Expr{ + { + Id: 6, + ExprKind: &expr.Expr_SelectExpr{ + SelectExpr: &expr.Expr_Select{ + Operand: &expr.Expr{ + Id: 5, + ExprKind: &expr.Expr_IdentExpr{ + IdentExpr: &expr.Expr_Ident{ + Name: "response", + }, + }, + }, + Field: "code", + }, + }, + }, + { + Id: 8, + ExprKind: &expr.Expr_ConstExpr{ + ConstExpr: &expr.Constant{ + ConstantKind: &expr.Constant_Int64Value{ + Int64Value: int64(codeRange.End), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } + if err := matcher.ValidateAll(); err != nil { + return nil, err + } + + if pb, err = anypb.New(matcher); err != nil { + return nil, err + } + + return &cncfv3.TypedExtensionConfig{ + Name: "cel-matcher", + TypedConfig: pb, + }, nil +} + +func (c *customResponse) buildAction(r ir.ResponseOverrideRule) (*matcherv3.Matcher_OnMatch_Action, error) { + response := &policyv3.LocalResponsePolicy{ + Body: &corev3.DataSource{ + Specifier: &corev3.DataSource_InlineString{ + InlineString: r.Response.Body, + }, + }, + } + + if r.Response.ContentType != nil && *r.Response.ContentType != "" { + response.ResponseHeadersToAdd = append(response.ResponseHeadersToAdd, &corev3.HeaderValueOption{ + Header: &corev3.HeaderValue{ + Key: "Content-Type", + Value: *r.Response.ContentType, + }, + AppendAction: corev3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, + }) + } + + var ( + pb *anypb.Any + err error + ) + + if err := response.ValidateAll(); err != nil { + return nil, err + } + + if pb, err = anypb.New(response); err != nil { + return nil, err + } + + return &matcherv3.Matcher_OnMatch_Action{ + Action: &cncfv3.TypedExtensionConfig{ + Name: r.Name, + TypedConfig: pb, + }, + }, nil +} + +// routeContainsResponseOverride returns true if ResponseOverride exists for the provided route. +func (c *customResponse) routeContainsResponseOverride(irRoute *ir.HTTPRoute) bool { + if irRoute != nil && + irRoute.Traffic != nil && + irRoute.Traffic.ResponseOverride != nil { + return true + } + return false +} + +func (c *customResponse) patchResources(tCtx *types.ResourceVersionTable, + routes []*ir.HTTPRoute, +) error { + return nil +} + +// patchRoute patches the provided route with the customResponse config if applicable. +// Note: this method enables the corresponding customResponse filter for the provided route. +func (c *customResponse) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute) error { + if route == nil { + return errors.New("xds route is nil") + } + if irRoute == nil { + return errors.New("ir route is nil") + } + if irRoute.Traffic == nil || irRoute.Traffic.ResponseOverride == nil { + return nil + } + filterName := c.customResponseFilterName(irRoute.Traffic.ResponseOverride) + if err := enableFilterOnRoute(route, filterName); err != nil { + return err + } + return nil +} diff --git a/internal/xds/translator/oidc.go b/internal/xds/translator/oidc.go index 963b7c8046d..e4e7b4a0216 100644 --- a/internal/xds/translator/oidc.go +++ b/internal/xds/translator/oidc.go @@ -53,9 +53,9 @@ func (*oidc) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListe continue } - // Only generates one BasicAuth Envoy filter for each unique name. + // Only generates one OAuth2 Envoy filter for each unique name. // For example, if there are two routes under the same gateway with the - // same BasicAuth config, only one BasicAuth filter will be generated. + // same OAuth2 config, only one OAuth2 filter will be generated. if hcmContainsFilter(mgr, oauth2FilterName(route.Security.OIDC)) { continue } diff --git a/internal/xds/translator/testdata/in/xds-ir/custom-response.yaml b/internal/xds/translator/testdata/in/xds-ir/custom-response.yaml new file mode 100644 index 00000000000..cb00ac65af9 --- /dev/null +++ b/internal/xds/translator/testdata/in/xds-ir/custom-response.yaml @@ -0,0 +1,56 @@ +http: + - address: 0.0.0.0 + hostnames: + - "*" + isHTTP2: false + metadata: + kind: Gateway + name: gateway-1 + namespace: default + sectionName: http + name: default/gateway-1/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + protocol: HTTP + weight: 1 + hostname: "*" + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/-1/* + traffic: + responseOverride: + name: backendtrafficpolicy/default/policy-for-gateway + rules: + - match: + statusCodes: + - value: 404 + name: backendtrafficpolicy/default/policy-for-gateway/responseoverride/rule/0 + response: + body: gateway-1 Not Found + contentType: text/plain + - match: + statusCodes: + - value: 500 + - range: + end: 511 + start: 501 + name: backendtrafficpolicy/default/policy-for-gateway/responseoverride/rule/1 + response: + body: | + { + "error": "Internal Server Error" + } + contentType: application/json diff --git a/internal/xds/translator/testdata/out/xds-ir/custom-response.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/custom-response.clusters.yaml new file mode 100644 index 00000000000..9714612e3de --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/custom-response.clusters.yaml @@ -0,0 +1,17 @@ +- circuitBreakers: + thresholds: + - maxRetries: 1024 + commonLbConfig: + localityWeightedLbConfig: {} + connectTimeout: 10s + dnsLookupFamily: V4_ONLY + edsClusterConfig: + edsConfig: + ads: {} + resourceApiVersion: V3 + serviceName: httproute/default/httproute-1/rule/0 + lbPolicy: LEAST_REQUEST + name: httproute/default/httproute-1/rule/0 + outlierDetection: {} + perConnectionBufferLimitBytes: 32768 + type: EDS diff --git a/internal/xds/translator/testdata/out/xds-ir/custom-response.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/custom-response.endpoints.yaml new file mode 100644 index 00000000000..29bb6b4e444 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/custom-response.endpoints.yaml @@ -0,0 +1,12 @@ +- clusterName: httproute/default/httproute-1/rule/0 + endpoints: + - lbEndpoints: + - endpoint: + address: + socketAddress: + address: 7.7.7.7 + portValue: 8080 + loadBalancingWeight: 1 + loadBalancingWeight: 1 + locality: + region: httproute/default/httproute-1/rule/0/backend/0 diff --git a/internal/xds/translator/testdata/out/xds-ir/custom-response.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/custom-response.listeners.yaml new file mode 100644 index 00000000000..19c56586960 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/custom-response.listeners.yaml @@ -0,0 +1,130 @@ +- address: + socketAddress: + address: 0.0.0.0 + portValue: 10080 + defaultFilterChain: + filters: + - name: envoy.filters.network.http_connection_manager + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + commonHttpProtocolOptions: + headersWithUnderscoresAction: REJECT_REQUEST + http2ProtocolOptions: + initialConnectionWindowSize: 1048576 + initialStreamWindowSize: 65536 + maxConcurrentStreams: 100 + httpFilters: + - disabled: true + name: envoy.filters.http.custom_response/backendtrafficpolicy/default/policy-for-gateway + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.custom_response.v3.CustomResponse + customResponseMatcher: + matcherList: + matchers: + - onMatch: + action: + name: backendtrafficpolicy/default/policy-for-gateway/responseoverride/rule/0 + typedConfig: + '@type': type.googleapis.com/envoy.extensions.http.custom_response.local_response_policy.v3.LocalResponsePolicy + body: + inlineString: gateway-1 Not Found + responseHeadersToAdd: + - appendAction: OVERWRITE_IF_EXISTS_OR_ADD + header: + key: Content-Type + value: text/plain + predicate: + singlePredicate: + input: + name: http-response-status-code-match-input + typedConfig: + '@type': type.googleapis.com/envoy.type.matcher.v3.HttpResponseStatusCodeMatchInput + valueMatch: + exact: "404" + - onMatch: + action: + name: backendtrafficpolicy/default/policy-for-gateway/responseoverride/rule/1 + typedConfig: + '@type': type.googleapis.com/envoy.extensions.http.custom_response.local_response_policy.v3.LocalResponsePolicy + body: + inlineString: | + { + "error": "Internal Server Error" + } + responseHeadersToAdd: + - appendAction: OVERWRITE_IF_EXISTS_OR_ADD + header: + key: Content-Type + value: application/json + predicate: + orMatcher: + predicate: + - singlePredicate: + input: + name: http-response-status-code-match-input + typedConfig: + '@type': type.googleapis.com/envoy.type.matcher.v3.HttpResponseStatusCodeMatchInput + valueMatch: + exact: "500" + - singlePredicate: + customMatch: + name: cel-matcher + typedConfig: + '@type': type.googleapis.com/xds.type.matcher.v3.CelMatcher + exprMatch: + parsedExpr: + expr: + callExpr: + args: + - callExpr: + args: + - id: "2" + selectExpr: + field: code + operand: + id: "1" + identExpr: + name: response + - constExpr: + int64Value: "501" + id: "4" + function: _>=_ + id: "3" + - callExpr: + args: + - id: "6" + selectExpr: + field: code + operand: + id: "5" + identExpr: + name: response + - constExpr: + int64Value: "511" + id: "8" + function: _<=_ + id: "7" + function: _&&_ + id: "9" + input: + name: http-attributes-cel-match-input + typedConfig: + '@type': type.googleapis.com/xds.type.matcher.v3.HttpAttributesCelMatchInput + - name: envoy.filters.http.router + typedConfig: + '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + suppressEnvoyHeaders: true + mergeSlashes: true + normalizePath: true + pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT + rds: + configSource: + ads: {} + resourceApiVersion: V3 + routeConfigName: default/gateway-1/http + serverHeaderTransformation: PASS_THROUGH + statPrefix: http-10080 + useRemoteAddress: true + name: default/gateway-1/http + name: default/gateway-1/http + perConnectionBufferLimitBytes: 32768 diff --git a/internal/xds/translator/testdata/out/xds-ir/custom-response.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/custom-response.routes.yaml new file mode 100644 index 00000000000..8262bb6f325 --- /dev/null +++ b/internal/xds/translator/testdata/out/xds-ir/custom-response.routes.yaml @@ -0,0 +1,33 @@ +- ignorePortInHostMatching: true + name: default/gateway-1/http + virtualHosts: + - domains: + - '*' + metadata: + filterMetadata: + envoy-gateway: + resources: + - kind: Gateway + name: gateway-1 + namespace: default + sectionName: http + name: default/gateway-1/http/* + routes: + - match: + prefix: / + metadata: + filterMetadata: + envoy-gateway: + resources: + - kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/-1/* + route: + cluster: httproute/default/httproute-1/rule/0 + upgradeConfigs: + - upgradeType: websocket + typedPerFilterConfig: + envoy.filters.http.custom_response/backendtrafficpolicy/default/policy-for-gateway: + '@type': type.googleapis.com/envoy.config.route.v3.FilterConfig + config: {} diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index fe361099a84..4f562fce611 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -483,6 +483,7 @@ _Appears in:_ | `rateLimit` | _[RateLimitSpec](#ratelimitspec)_ | false | RateLimit allows the user to limit the number of incoming requests
to a predefined value based on attributes within the traffic flow. | | `faultInjection` | _[FaultInjection](#faultinjection)_ | false | FaultInjection defines the fault injection policy to be applied. This configuration can be used to
inject delays and abort requests to mimic failure scenarios such as service failures and overloads | | `useClientProtocol` | _boolean_ | false | UseClientProtocol configures Envoy to prefer sending requests to backends using
the same HTTP protocol that the incoming request used. Defaults to false, which means
that Envoy will use the protocol indicated by the attached BackendRef. | +| `responseOverride` | _[ResponseOverride](#responseoverride) array_ | false | ResponseOverride defines the configuration to override specific responses with a custom one.
If multiple configurations are specified, the first one to match wins. | #### BasicAuth @@ -866,7 +867,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | | `contentType` | _string_ | false | Content Type of the response. This will be set in the Content-Type header. | -| `body` | _[CustomResponseBody](#customresponsebody)_ | false | Body of the Custom Response | +| `body` | _[CustomResponseBody](#customresponsebody)_ | true | Body of the Custom Response | #### CustomResponseBody @@ -881,9 +882,9 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | -| `type` | _[ResponseValueType](#responsevaluetype)_ | true | Type is the type of method to use to read the body value. | +| `type` | _[ResponseValueType](#responsevaluetype)_ | true | Type is the type of method to use to read the body value.
Valid values are Inline and ValueRef, default is Inline. | | `inline` | _string_ | false | Inline contains the value as an inline string. | -| `valueRef` | _[LocalObjectReference](#localobjectreference)_ | false | ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported. | +| `valueRef` | _[LocalObjectReference](#localobjectreference)_ | false | ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported.

The value of key `response.body` in the ConfigMap will be used as the response body.
If the key is not found, the first value in the ConfigMap will be used. | #### CustomResponseMatch @@ -897,7 +898,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | -| `statusCode` | _[StatusCodeMatch](#statuscodematch) array_ | true | Status code to match on. The match evaluates to true if any of the matches are successful. | +| `statusCodes` | _[StatusCodeMatch](#statuscodematch) array_ | true | Status code to match on. The match evaluates to true if any of the matches are successful. | #### CustomTag @@ -1026,6 +1027,7 @@ _Appears in:_ | `envoy.filters.http.rbac` | EnvoyFilterRBAC defines the Envoy RBAC filter.
| | `envoy.filters.http.local_ratelimit` | EnvoyFilterLocalRateLimit defines the Envoy HTTP local rate limit filter.
| | `envoy.filters.http.ratelimit` | EnvoyFilterRateLimit defines the Envoy HTTP rate limit filter.
| +| `envoy.filters.http.custom_response` | EnvoyFilterCustomResponse defines the Envoy HTTP custom response filter.
| | `envoy.filters.http.router` | EnvoyFilterRouter defines the Envoy HTTP router filter.
| @@ -1479,7 +1481,7 @@ _Appears in:_ | `extraArgs` | _string array_ | false | ExtraArgs defines additional command line options that are provided to Envoy.
More info: https://www.envoyproxy.io/docs/envoy/latest/operations/cli#command-line-options
Note: some command line options are used internally(e.g. --log-level) so they cannot be provided here. | | `mergeGateways` | _boolean_ | false | MergeGateways defines if Gateway resources should be merged onto the same Envoy Proxy Infrastructure.
Setting this field to true would merge all Gateway Listeners under the parent Gateway Class.
This means that the port, protocol and hostname tuple must be unique for every listener.
If a duplicate listener is detected, the newer listener (based on timestamp) will be rejected and its status will be updated with a "Accepted=False" condition. | | `shutdown` | _[ShutdownConfig](#shutdownconfig)_ | false | Shutdown defines configuration for graceful envoy shutdown process. | -| `filterOrder` | _[FilterPosition](#filterposition) array_ | false | FilterOrder defines the order of filters in the Envoy proxy's HTTP filter chain.
The FilterPosition in the list will be applied in the order they are defined.
If unspecified, the default filter order is applied.
Default filter order is:

- envoy.filters.http.health_check

- envoy.filters.http.fault

- envoy.filters.http.cors

- envoy.filters.http.ext_authz

- envoy.filters.http.basic_auth

- envoy.filters.http.oauth2

- envoy.filters.http.jwt_authn

- envoy.filters.http.stateful_session

- envoy.filters.http.ext_proc

- envoy.filters.http.wasm

- envoy.filters.http.rbac

- envoy.filters.http.local_ratelimit

- envoy.filters.http.ratelimit

- envoy.filters.http.router

Note: "envoy.filters.http.router" cannot be reordered, it's always the last filter in the chain. | +| `filterOrder` | _[FilterPosition](#filterposition) array_ | false | FilterOrder defines the order of filters in the Envoy proxy's HTTP filter chain.
The FilterPosition in the list will be applied in the order they are defined.
If unspecified, the default filter order is applied.
Default filter order is:

- envoy.filters.http.health_check

- envoy.filters.http.fault

- envoy.filters.http.cors

- envoy.filters.http.ext_authz

- envoy.filters.http.basic_auth

- envoy.filters.http.oauth2

- envoy.filters.http.jwt_authn

- envoy.filters.http.stateful_session

- envoy.filters.http.ext_proc

- envoy.filters.http.wasm

- envoy.filters.http.rbac

- envoy.filters.http.local_ratelimit

- envoy.filters.http.ratelimit

- envoy.filters.http.custom_response

- envoy.filters.http.router

Note: "envoy.filters.http.router" cannot be reordered, it's always the last filter in the chain. | | `backendTLS` | _[BackendTLSConfig](#backendtlsconfig)_ | false | BackendTLS is the TLS configuration for the Envoy proxy to use when connecting to backends.
These settings are applied on backends for which TLS policies are specified. | @@ -3573,6 +3575,10 @@ ResponseValueType defines the types of values for the response body supported by _Appears in:_ - [CustomResponseBody](#customresponsebody) +| Value | Description | +| ----- | ----------- | +| `Inline` | ResponseValueTypeInline defines the "Inline" response body type.
| +| `ValueRef` | ResponseValueTypeValueRef defines the "ValueRef" response body type.
| @@ -3821,16 +3827,16 @@ _Appears in:_ - +StatusCodeMatch defines the configuration for matching a status code. _Appears in:_ - [CustomResponseMatch](#customresponsematch) | Field | Type | Required | Description | | --- | --- | --- | --- | -| `type` | _[StatusCodeValueType](#statuscodevaluetype)_ | true | Type is the type of value. | -| `value` | _string_ | false | Value contains the value of the status code. | -| `range` | _[StatusCodeRange](#statuscoderange)_ | false | ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported. | +| `type` | _[StatusCodeValueType](#statuscodevaluetype)_ | true | Type is the type of value.
Valid values are Value and Range, default is Value. | +| `value` | _integer_ | false | Value contains the value of the status code. | +| `range` | _[StatusCodeRange](#statuscoderange)_ | false | Range contains the range of status codes. | #### StatusCodeRange @@ -3857,6 +3863,10 @@ StatusCodeValueType defines the types of values for the status code match suppor _Appears in:_ - [StatusCodeMatch](#statuscodematch) +| Value | Description | +| ----- | ----------- | +| `Value` | StatusCodeValueTypeValue defines the "Value" status code match type.
| +| `Range` | StatusCodeValueTypeRange defines the "Range" status code match type.
| #### StringMatch diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index fe361099a84..4f562fce611 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -483,6 +483,7 @@ _Appears in:_ | `rateLimit` | _[RateLimitSpec](#ratelimitspec)_ | false | RateLimit allows the user to limit the number of incoming requests
to a predefined value based on attributes within the traffic flow. | | `faultInjection` | _[FaultInjection](#faultinjection)_ | false | FaultInjection defines the fault injection policy to be applied. This configuration can be used to
inject delays and abort requests to mimic failure scenarios such as service failures and overloads | | `useClientProtocol` | _boolean_ | false | UseClientProtocol configures Envoy to prefer sending requests to backends using
the same HTTP protocol that the incoming request used. Defaults to false, which means
that Envoy will use the protocol indicated by the attached BackendRef. | +| `responseOverride` | _[ResponseOverride](#responseoverride) array_ | false | ResponseOverride defines the configuration to override specific responses with a custom one.
If multiple configurations are specified, the first one to match wins. | #### BasicAuth @@ -866,7 +867,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | | `contentType` | _string_ | false | Content Type of the response. This will be set in the Content-Type header. | -| `body` | _[CustomResponseBody](#customresponsebody)_ | false | Body of the Custom Response | +| `body` | _[CustomResponseBody](#customresponsebody)_ | true | Body of the Custom Response | #### CustomResponseBody @@ -881,9 +882,9 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | -| `type` | _[ResponseValueType](#responsevaluetype)_ | true | Type is the type of method to use to read the body value. | +| `type` | _[ResponseValueType](#responsevaluetype)_ | true | Type is the type of method to use to read the body value.
Valid values are Inline and ValueRef, default is Inline. | | `inline` | _string_ | false | Inline contains the value as an inline string. | -| `valueRef` | _[LocalObjectReference](#localobjectreference)_ | false | ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported. | +| `valueRef` | _[LocalObjectReference](#localobjectreference)_ | false | ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported.

The value of key `response.body` in the ConfigMap will be used as the response body.
If the key is not found, the first value in the ConfigMap will be used. | #### CustomResponseMatch @@ -897,7 +898,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | -| `statusCode` | _[StatusCodeMatch](#statuscodematch) array_ | true | Status code to match on. The match evaluates to true if any of the matches are successful. | +| `statusCodes` | _[StatusCodeMatch](#statuscodematch) array_ | true | Status code to match on. The match evaluates to true if any of the matches are successful. | #### CustomTag @@ -1026,6 +1027,7 @@ _Appears in:_ | `envoy.filters.http.rbac` | EnvoyFilterRBAC defines the Envoy RBAC filter.
| | `envoy.filters.http.local_ratelimit` | EnvoyFilterLocalRateLimit defines the Envoy HTTP local rate limit filter.
| | `envoy.filters.http.ratelimit` | EnvoyFilterRateLimit defines the Envoy HTTP rate limit filter.
| +| `envoy.filters.http.custom_response` | EnvoyFilterCustomResponse defines the Envoy HTTP custom response filter.
| | `envoy.filters.http.router` | EnvoyFilterRouter defines the Envoy HTTP router filter.
| @@ -1479,7 +1481,7 @@ _Appears in:_ | `extraArgs` | _string array_ | false | ExtraArgs defines additional command line options that are provided to Envoy.
More info: https://www.envoyproxy.io/docs/envoy/latest/operations/cli#command-line-options
Note: some command line options are used internally(e.g. --log-level) so they cannot be provided here. | | `mergeGateways` | _boolean_ | false | MergeGateways defines if Gateway resources should be merged onto the same Envoy Proxy Infrastructure.
Setting this field to true would merge all Gateway Listeners under the parent Gateway Class.
This means that the port, protocol and hostname tuple must be unique for every listener.
If a duplicate listener is detected, the newer listener (based on timestamp) will be rejected and its status will be updated with a "Accepted=False" condition. | | `shutdown` | _[ShutdownConfig](#shutdownconfig)_ | false | Shutdown defines configuration for graceful envoy shutdown process. | -| `filterOrder` | _[FilterPosition](#filterposition) array_ | false | FilterOrder defines the order of filters in the Envoy proxy's HTTP filter chain.
The FilterPosition in the list will be applied in the order they are defined.
If unspecified, the default filter order is applied.
Default filter order is:

- envoy.filters.http.health_check

- envoy.filters.http.fault

- envoy.filters.http.cors

- envoy.filters.http.ext_authz

- envoy.filters.http.basic_auth

- envoy.filters.http.oauth2

- envoy.filters.http.jwt_authn

- envoy.filters.http.stateful_session

- envoy.filters.http.ext_proc

- envoy.filters.http.wasm

- envoy.filters.http.rbac

- envoy.filters.http.local_ratelimit

- envoy.filters.http.ratelimit

- envoy.filters.http.router

Note: "envoy.filters.http.router" cannot be reordered, it's always the last filter in the chain. | +| `filterOrder` | _[FilterPosition](#filterposition) array_ | false | FilterOrder defines the order of filters in the Envoy proxy's HTTP filter chain.
The FilterPosition in the list will be applied in the order they are defined.
If unspecified, the default filter order is applied.
Default filter order is:

- envoy.filters.http.health_check

- envoy.filters.http.fault

- envoy.filters.http.cors

- envoy.filters.http.ext_authz

- envoy.filters.http.basic_auth

- envoy.filters.http.oauth2

- envoy.filters.http.jwt_authn

- envoy.filters.http.stateful_session

- envoy.filters.http.ext_proc

- envoy.filters.http.wasm

- envoy.filters.http.rbac

- envoy.filters.http.local_ratelimit

- envoy.filters.http.ratelimit

- envoy.filters.http.custom_response

- envoy.filters.http.router

Note: "envoy.filters.http.router" cannot be reordered, it's always the last filter in the chain. | | `backendTLS` | _[BackendTLSConfig](#backendtlsconfig)_ | false | BackendTLS is the TLS configuration for the Envoy proxy to use when connecting to backends.
These settings are applied on backends for which TLS policies are specified. | @@ -3573,6 +3575,10 @@ ResponseValueType defines the types of values for the response body supported by _Appears in:_ - [CustomResponseBody](#customresponsebody) +| Value | Description | +| ----- | ----------- | +| `Inline` | ResponseValueTypeInline defines the "Inline" response body type.
| +| `ValueRef` | ResponseValueTypeValueRef defines the "ValueRef" response body type.
| @@ -3821,16 +3827,16 @@ _Appears in:_ - +StatusCodeMatch defines the configuration for matching a status code. _Appears in:_ - [CustomResponseMatch](#customresponsematch) | Field | Type | Required | Description | | --- | --- | --- | --- | -| `type` | _[StatusCodeValueType](#statuscodevaluetype)_ | true | Type is the type of value. | -| `value` | _string_ | false | Value contains the value of the status code. | -| `range` | _[StatusCodeRange](#statuscoderange)_ | false | ValueRef contains the contents of the body
specified as a local object reference.
Only a reference to ConfigMap is supported. | +| `type` | _[StatusCodeValueType](#statuscodevaluetype)_ | true | Type is the type of value.
Valid values are Value and Range, default is Value. | +| `value` | _integer_ | false | Value contains the value of the status code. | +| `range` | _[StatusCodeRange](#statuscoderange)_ | false | Range contains the range of status codes. | #### StatusCodeRange @@ -3857,6 +3863,10 @@ StatusCodeValueType defines the types of values for the status code match suppor _Appears in:_ - [StatusCodeMatch](#statuscodematch) +| Value | Description | +| ----- | ----------- | +| `Value` | StatusCodeValueTypeValue defines the "Value" status code match type.
| +| `Range` | StatusCodeValueTypeRange defines the "Range" status code match type.
| #### StringMatch diff --git a/test/cel-validation/backendtrafficpolicy_test.go b/test/cel-validation/backendtrafficpolicy_test.go index 49f033eb6ae..d5e6a1b2d1f 100644 --- a/test/cel-validation/backendtrafficpolicy_test.go +++ b/test/cel-validation/backendtrafficpolicy_test.go @@ -1257,6 +1257,251 @@ func TestBackendTrafficPolicyTarget(t *testing.T) { }, wantErrors: []string{}, }, + { + desc: "both targetref and targetrefs specified", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ResponseOverride: []*egv1a1.ResponseOverride{ + { + Match: egv1a1.CustomResponseMatch{ + StatusCodes: []egv1a1.StatusCodeMatch{ + { + Type: ptr.To(egv1a1.StatusCodeValueTypeValue), + Range: &egv1a1.StatusCodeRange{ + Start: 100, + End: 200, + }, + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{ + "value must be set for type Value", + }, + }, + { + desc: "both targetref and targetrefs specified", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ResponseOverride: []*egv1a1.ResponseOverride{ + { + Match: egv1a1.CustomResponseMatch{ + StatusCodes: []egv1a1.StatusCodeMatch{ + { + Range: &egv1a1.StatusCodeRange{ + Start: 100, + End: 200, + }, + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{ + "value must be set for type Value", + }, + }, + { + desc: "both targetref and targetrefs specified", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ResponseOverride: []*egv1a1.ResponseOverride{ + { + Match: egv1a1.CustomResponseMatch{ + StatusCodes: []egv1a1.StatusCodeMatch{ + { + Type: ptr.To(egv1a1.StatusCodeValueTypeRange), + Value: ptr.To(100), + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{ + "range must be set for type Range", + }, + }, + { + desc: "both targetref and targetrefs specified", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ResponseOverride: []*egv1a1.ResponseOverride{ + { + Match: egv1a1.CustomResponseMatch{ + StatusCodes: []egv1a1.StatusCodeMatch{ + { + Type: ptr.To(egv1a1.StatusCodeValueTypeRange), + Range: &egv1a1.StatusCodeRange{ + Start: 200, + End: 100, + }, + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{ + "end must be greater than start", + }, + }, + { + desc: "both targetref and targetrefs specified", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ResponseOverride: []*egv1a1.ResponseOverride{ + { + Match: egv1a1.CustomResponseMatch{ + StatusCodes: []egv1a1.StatusCodeMatch{ + { + Value: ptr.To(100), + }, + }, + }, + Response: egv1a1.CustomResponse{ + Body: egv1a1.CustomResponseBody{ + ValueRef: &gwapiv1a2.LocalObjectReference{ + Kind: gwapiv1a2.Kind("ConfigMap"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{ + "inline must be set for type Inline", + }, + }, + { + desc: "both targetref and targetrefs specified", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ResponseOverride: []*egv1a1.ResponseOverride{ + { + Match: egv1a1.CustomResponseMatch{ + StatusCodes: []egv1a1.StatusCodeMatch{ + { + Value: ptr.To(100), + }, + }, + }, + Response: egv1a1.CustomResponse{ + Body: egv1a1.CustomResponseBody{ + Type: ptr.To(egv1a1.ResponseValueTypeValueRef), + Inline: ptr.To("foo"), + }, + }, + }, + }, + } + }, + wantErrors: []string{ + "valueRef must be set for type ValueRef", + }, + }, + { + desc: "both targetref and targetrefs specified", + mutate: func(btp *egv1a1.BackendTrafficPolicy) { + btp.Spec = egv1a1.BackendTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + ResponseOverride: []*egv1a1.ResponseOverride{ + { + Match: egv1a1.CustomResponseMatch{ + StatusCodes: []egv1a1.StatusCodeMatch{ + { + Value: ptr.To(100), + }, + }, + }, + Response: egv1a1.CustomResponse{ + Body: egv1a1.CustomResponseBody{ + Type: ptr.To(egv1a1.ResponseValueTypeValueRef), + ValueRef: &gwapiv1a2.LocalObjectReference{ + Kind: gwapiv1a2.Kind("Foo"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + }, + }, + } + }, + wantErrors: []string{ + "only ConfigMap is supported for ValueRe", + }, + }, } for _, tc := range cases { diff --git a/test/e2e/testdata/response-override.yaml b/test/e2e/testdata/response-override.yaml new file mode 100644 index 00000000000..084747aaa6c --- /dev/null +++ b/test/e2e/testdata/response-override.yaml @@ -0,0 +1,62 @@ +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: response-override + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: infra-backend-v1 + port: 8080 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: response-override-config + namespace: gateway-conformance-infra +data: + response.body: '{"error": "Internal Server Error"}' +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: response-override + namespace: gateway-conformance-infra +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: response-override + responseOverride: + - match: + statusCodes: + - type: Value + value: 404 + response: + contentType: text/plain + body: + type: Inline + inline: "Oops! Your request is not found." + - match: + statusCodes: + - type: Value + value: 500 + - type: Range + range: + start: 501 + end: 511 + response: + contentType: application/json + body: + type: ValueRef + valueRef: + group: "" + kind: ConfigMap + name: response-override-config diff --git a/test/e2e/tests/response-override.go b/test/e2e/tests/response-override.go new file mode 100644 index 00000000000..b21db88e242 --- /dev/null +++ b/test/e2e/tests/response-override.go @@ -0,0 +1,83 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build e2e + +package tests + +import ( + "fmt" + "io" + "net/http" + "net/url" + "testing" + + "k8s.io/apimachinery/pkg/types" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + httputils "sigs.k8s.io/gateway-api/conformance/utils/http" + "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api/conformance/utils/suite" + + "github.com/envoyproxy/gateway/internal/gatewayapi" + "github.com/envoyproxy/gateway/internal/gatewayapi/resource" +) + +func init() { + ConformanceTests = append(ConformanceTests, ResponseOverrideTest) +} + +var ResponseOverrideTest = suite.ConformanceTest{ + ShortName: "ResponseOverrideSpecificUser", + Description: "Response Override", + Manifests: []string{"testdata/response-override.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("response override", func(t *testing.T) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "response-override", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + ancestorRef := gwapiv1a2.ParentReference{ + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), + Kind: gatewayapi.KindPtr(resource.KindGateway), + Namespace: gatewayapi.NamespacePtr(gwNN.Namespace), + Name: gwapiv1.ObjectName(gwNN.Name), + } + BackendTrafficPolicyMustBeAccepted(t, suite.Client, types.NamespacedName{Name: "response-override", Namespace: ns}, suite.ControllerName, ancestorRef) + verifyResponseOverride(t, gwAddr, 404, "text/plain", "Oops! Your request is not found.") + verifyResponseOverride(t, gwAddr, 500, "application/json", `{"error": "Internal Server Error"}`) + }) + }, +} + +func verifyResponseOverride(t *testing.T, gwAddr string, statusCode int, expectedContentType string, expectedBody string) { + reqURL := url.URL{ + Scheme: "http", + Host: httputils.CalculateHost(t, gwAddr, "http"), + Path: fmt.Sprintf("/status/%d", statusCode), + } + + rsp, err := http.Get(reqURL.String()) + if err != nil { + t.Fatalf("failed to get response: %v", err) + } + + // Verify that the response body is overridden + defer rsp.Body.Close() + body, err := io.ReadAll(rsp.Body) + if err != nil { + t.Fatalf("failed to read response body: %v", err) + } + if string(body) != expectedBody { + t.Errorf("expected response body to be %s but got %s", expectedBody, string(body)) + } + + // Verify that the content type is overridden + contentType := rsp.Header.Get("Content-Type") + if contentType != expectedContentType { + t.Errorf("expected content type to be %s but got %s", expectedContentType, contentType) + } +} From 31ff8ffd1dfefa9c0acd7d67a6aee76e4d30abbf Mon Sep 17 00:00:00 2001 From: Guy Daich Date: Tue, 22 Oct 2024 05:32:16 -0500 Subject: [PATCH 49/56] fix: enforce connection limit value (#4458) * fix: enforce connection limit value Signed-off-by: Guy Daich * rm omitempty Signed-off-by: Guy Daich * fix cel Signed-off-by: Guy Daich * remove validation Signed-off-by: Guy Daich --------- Signed-off-by: Guy Daich Co-authored-by: zirain --- api/v1alpha1/connection_types.go | 5 +- ...y.envoyproxy.io_clienttrafficpolicies.yaml | 5 +- site/content/en/latest/api/extension_types.md | 2 +- site/content/zh/latest/api/extension_types.md | 2 +- .../clienttrafficpolicy_test.go | 46 +++++++++++++++++++ 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/api/v1alpha1/connection_types.go b/api/v1alpha1/connection_types.go index 6f27794748b..efb24dc3bb1 100644 --- a/api/v1alpha1/connection_types.go +++ b/api/v1alpha1/connection_types.go @@ -66,10 +66,9 @@ type BackendConnection struct { type ConnectionLimit struct { // Value of the maximum concurrent connections limit. // When the limit is reached, incoming connections will be closed after the CloseDelay duration. - // Default: unlimited. // - // +kubebuilder:validation:Minimum=0 - Value int64 `json:"value,omitempty"` + // +kubebuilder:validation:Minimum=1 + Value int64 `json:"value"` // CloseDelay defines the delay to use before closing connections that are rejected // once the limit value is reached. diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index 582486e706f..3e626f3f88a 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -125,10 +125,11 @@ spec: description: |- Value of the maximum concurrent connections limit. When the limit is reached, incoming connections will be closed after the CloseDelay duration. - Default: unlimited. format: int64 - minimum: 0 + minimum: 1 type: integer + required: + - value type: object socketBufferLimit: allOf: diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 4f562fce611..c183a4f0b8f 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -782,7 +782,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | -| `value` | _integer_ | true | Value of the maximum concurrent connections limit.
When the limit is reached, incoming connections will be closed after the CloseDelay duration.
Default: unlimited. | +| `value` | _integer_ | true | Value of the maximum concurrent connections limit.
When the limit is reached, incoming connections will be closed after the CloseDelay duration. | | `closeDelay` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ | false | CloseDelay defines the delay to use before closing connections that are rejected
once the limit value is reached.
Default: none. | diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index 4f562fce611..c183a4f0b8f 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -782,7 +782,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | -| `value` | _integer_ | true | Value of the maximum concurrent connections limit.
When the limit is reached, incoming connections will be closed after the CloseDelay duration.
Default: unlimited. | +| `value` | _integer_ | true | Value of the maximum concurrent connections limit.
When the limit is reached, incoming connections will be closed after the CloseDelay duration. | | `closeDelay` | _[Duration](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1.Duration)_ | false | CloseDelay defines the delay to use before closing connections that are rejected
once the limit value is reached.
Default: none. | diff --git a/test/cel-validation/clienttrafficpolicy_test.go b/test/cel-validation/clienttrafficpolicy_test.go index 14dd915d78a..3558d1848f9 100644 --- a/test/cel-validation/clienttrafficpolicy_test.go +++ b/test/cel-validation/clienttrafficpolicy_test.go @@ -314,6 +314,52 @@ func TestClientTrafficPolicyTarget(t *testing.T) { "spec.connection.bufferLimit: Invalid value: \"15m\": spec.connection.bufferLimit in body should match '^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$', : Invalid value: \"\"", }, }, + { + desc: "invalid Connection Limit Empty", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + Connection: &egv1a1.ClientConnection{ + ConnectionLimit: &egv1a1.ConnectionLimit{}, + }, + } + }, + wantErrors: []string{ + "spec.connection.connectionLimit.value: Invalid value: 0: spec.connection.connectionLimit.value in body should be greater than or equal to 1", + }, + }, + { + desc: "invalid Connection Limit < 1", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + PolicyTargetReferences: egv1a1.PolicyTargetReferences{ + TargetRef: &gwapiv1a2.LocalPolicyTargetReferenceWithSectionName{ + LocalPolicyTargetReference: gwapiv1a2.LocalPolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + }, + Connection: &egv1a1.ClientConnection{ + ConnectionLimit: &egv1a1.ConnectionLimit{ + Value: -1, // Value: 0 is covered by existence test, as 0 is the nil value. + }, + }, + } + }, + wantErrors: []string{ + "spec.connection.connectionLimit.value: Invalid value: -1: spec.connection.connectionLimit.value in body should be greater than or equal to 1", + }, + }, { desc: "invalid InitialStreamWindowSize format", mutate: func(ctp *egv1a1.ClientTrafficPolicy) { From a5c3cd21fe39308778e028b79079596ce34c6c04 Mon Sep 17 00:00:00 2001 From: Huabing Zhao Date: Tue, 22 Oct 2024 19:36:42 +0800 Subject: [PATCH 50/56] Collect release note in each PR (#4473) * release note for v1.2 Signed-off-by: Huabing Zhao * collecting release note in each PR Signed-off-by: Huabing Zhao * collecting release note in each PR Signed-off-by: Huabing Zhao * collecting release note in each PR Signed-off-by: Huabing Zhao * collecting release note in each PR Signed-off-by: Huabing Zhao * fix lint Signed-off-by: Huabing Zhao * reorganize sections Signed-off-by: Huabing Zhao * minor wording Signed-off-by: Huabing Zhao * fix lint Signed-off-by: Huabing Zhao --------- Signed-off-by: Huabing Zhao --- .github/PULL_REQUEST_TEMPLATE.md | 8 +++++- release-notes/current.yaml | 29 ++++++++++++++++++++++ site/content/en/contributions/RELEASING.md | 14 ++++++++--- 3 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 release-notes/current.yaml diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 8dc586ce114..c50e9d08e25 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,6 @@ **What type of PR is this?** Fixes # + + +Release Notes: Yes/No diff --git a/release-notes/current.yaml b/release-notes/current.yaml new file mode 100644 index 00000000000..524802636e6 --- /dev/null +++ b/release-notes/current.yaml @@ -0,0 +1,29 @@ +date: Pending + +# Changes that are expected to cause an incompatibility with previous versions, such as deletions or modifications to existing APIs. +breaking changes: | + - Add a breaking change here + +# Updates addressing vulnerabilities, security flaws, or compliance requirements. +security updates: | + - Add a security update here + +# New features or capabilities added in this release. +new features: | + - Add a new feature here + +# Fixes for bugs identified in previous versions. +bug fixes: | + - Add a bug fix here + +# Enhancements that improve performance. +performance improvements: | + - Add a performance improvement here + +# Deprecated features or APIs. +deprecations: | + - Add a deprecation here + +# Other notable changes not covered by the above sections. +Other changes: | + - Add other changes here diff --git a/site/content/en/contributions/RELEASING.md b/site/content/en/contributions/RELEASING.md index 319bb73d057..70fff1c083b 100644 --- a/site/content/en/contributions/RELEASING.md +++ b/site/content/en/contributions/RELEASING.md @@ -29,7 +29,13 @@ export GITHUB_REMOTE=origin ``` 1. Clone the repo, checkout the `main` branch, ensure it’s up-to-date, and your local branch is clean. -2. Create a topic branch for adding the release notes and updating the [VERSION][] file with the release version. Refer to previous [release notes][] and [VERSION][] for additional details. +2. Create a topic branch for adding the release notes and updating the [VERSION][] file with the release version. Refer to previous [release notes][] and [VERSION][] for additional details. The latest changes are already accumulated in the current.yaml file. Copy the content of the current.yaml file to the release notes file and clear the current.yaml file. + + ```shell + echo "${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER}" > VERSION + ``` + + __Note:__ The release candidate version should be in the format `${MAJOR_VERSION}.${MINOR_VERSION}.0-rc.${RELEASE_CANDIDATE_NUMBER}`. 3. Sign, commit, and push your changes to your fork. 4. Submit a [Pull Request][] to merge the changes into the `main` branch. Do not proceed until your PR has merged and the [Build and Test][] has successfully completed. @@ -106,7 +112,7 @@ export GITHUB_REMOTE=origin ``` 1. Update the `Documentation` referred link on the menu in `site/hugo.toml`: - + **DON'T FORGOT TO MOVE IT UNDER `LATEST`** ```shell @@ -118,7 +124,7 @@ export GITHUB_REMOTE=origin ``` 1. Update `site/layouts/shortcodes/helm-version.html` base on latest minor version. - + ```console {{- $pagePrefix := (index (split $.Page.File.Dir "/") 0) -}} {{- with (eq $pagePrefix "latest") -}} @@ -133,7 +139,7 @@ export GITHUB_REMOTE=origin ``` 1. Update `site/layouts/shortcodes/yaml-version.html` base on latest minor version. - + ```console {{- $pagePrefix := (index (split $.Page.File.Dir "/") 0) -}} {{- with (eq $pagePrefix "latest") -}} From 291c0830bf9334febbbd162665e69dbb6fd57f51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Oct 2024 19:49:36 +0800 Subject: [PATCH 51/56] build(deps): bump github.com/replicatedhq/troubleshoot from 0.107.0 to 0.107.1 (#4487) build(deps): bump github.com/replicatedhq/troubleshoot Bumps [github.com/replicatedhq/troubleshoot](https://github.com/replicatedhq/troubleshoot) from 0.107.0 to 0.107.1. - [Release notes](https://github.com/replicatedhq/troubleshoot/releases) - [Commits](https://github.com/replicatedhq/troubleshoot/compare/v0.107.0...v0.107.1) --- updated-dependencies: - dependency-name: github.com/replicatedhq/troubleshoot dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a10aa68bbac..4d03c682219 100644 --- a/go.mod +++ b/go.mod @@ -63,7 +63,7 @@ require ( require ( github.com/docker/docker v27.3.1+incompatible - github.com/replicatedhq/troubleshoot v0.107.0 + github.com/replicatedhq/troubleshoot v0.107.1 google.golang.org/grpc v1.67.1 sigs.k8s.io/kubectl-validate v0.0.5-0.20240827210056-ce13d95db263 ) diff --git a/go.sum b/go.sum index dcf087e087a..d11cb03bbf0 100644 --- a/go.sum +++ b/go.sum @@ -717,8 +717,8 @@ github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= -github.com/replicatedhq/troubleshoot v0.107.0 h1:AzaKBxNKuIQpERyitjJ4EINZTz4vlUacW5QguZZkTww= -github.com/replicatedhq/troubleshoot v0.107.0/go.mod h1:AjaLpi9kidsFAuyAvBrvcG04w49WIDm7Iy6XyMVMv6U= +github.com/replicatedhq/troubleshoot v0.107.1 h1:Hx9VbVv1r3M5fiH2fPTeoZ8LNIxh5R/e6vpe2jBgPfc= +github.com/replicatedhq/troubleshoot v0.107.1/go.mod h1:6mZzcO/EWVBNXVnFdSHfPaoTnjcQdV3sq61NkBF60YE= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= From 1e6899d4445287e1df027024e8f3f1c3408c7add Mon Sep 17 00:00:00 2001 From: Rudrakh Panigrahi Date: Tue, 22 Oct 2024 17:22:49 +0530 Subject: [PATCH 52/56] docs: add docs for ratelimit invert match headers (#4483) * add docs for ratelimit invert match headers Signed-off-by: Rudrakh Panigrahi * add e2e test for local ratelimit header match with invert Signed-off-by: Rudrakh Panigrahi --------- Signed-off-by: Rudrakh Panigrahi --- api/v1alpha1/ratelimit_types.go | 1 - .../latest/tasks/traffic/global-rate-limit.md | 52 ++++- .../latest/tasks/traffic/local-rate-limit.md | 221 ++++++++++++++++++ ...ratelimit-header-invert-match-global.yaml} | 0 .../ratelimit-header-invert-match-local.yaml | 41 ++++ test/e2e/tests/local_ratelimit.go | 89 +++++++ test/e2e/tests/ratelimit.go | 8 +- 7 files changed, 403 insertions(+), 9 deletions(-) rename test/e2e/testdata/{ratelimit-header-invert-match.yaml => ratelimit-header-invert-match-global.yaml} (100%) create mode 100644 test/e2e/testdata/ratelimit-header-invert-match-local.yaml diff --git a/api/v1alpha1/ratelimit_types.go b/api/v1alpha1/ratelimit_types.go index d7a43751077..72382d699f1 100644 --- a/api/v1alpha1/ratelimit_types.go +++ b/api/v1alpha1/ratelimit_types.go @@ -166,7 +166,6 @@ type HeaderMatch struct { // TODO: zhaohuabing this type could be replaced with // // +optional // +kubebuilder:default=false - // +notImplementedHide Invert *bool `json:"invert,omitempty"` } diff --git a/site/content/en/latest/tasks/traffic/global-rate-limit.md b/site/content/en/latest/tasks/traffic/global-rate-limit.md index 15cc462dbf4..47eac33bc3e 100644 --- a/site/content/en/latest/tasks/traffic/global-rate-limit.md +++ b/site/content/en/latest/tasks/traffic/global-rate-limit.md @@ -433,11 +433,11 @@ server: envoy ``` -## Rate Limit Distinct Users +## Rate Limit Distinct Users Except Admin Here is an example of a rate limit implemented by the application developer to limit distinct users who can be differentiated based on the value in the `x-user-id` header. Here, user `one` (recognised from the traffic flow using the header `x-user-id` and value `one`) will be rate limited at 3 requests/hour -and so will user `two` (recognised from the traffic flow using the header `x-user-id` and value `two`). +and so will user `two` (recognised from the traffic flow using the header `x-user-id` and value `two`). But if `x-user-id` is `admin`, it will not be rate limited even beyond 3 requests/hour. {{< tabpane text=true >}} {{% tab header="Apply from stdin" %}} @@ -461,6 +461,9 @@ spec: - headers: - type: Distinct name: x-user-id + - name: x-user-id + value: admin + invert: true limit: requests: 3 unit: Hour @@ -636,6 +639,47 @@ transfer-encoding: chunked ``` +But when the value for header `x-user-id` is set to `admin` and 4 requests are sent, all 4 of them should respond with 200 OK. + +```shell +for i in {1..4}; do curl -I --header "Host: ratelimit.example" --header "x-user-id: admin" http://${GATEWAY_HOST}/get ; sleep 1; done +``` + +```console +HTTP/1.1 200 OK +content-type: application/json +x-content-type-options: nosniff +date: Wed, 08 Feb 2023 02:33:31 GMT +content-length: 460 +x-envoy-upstream-service-time: 4 +server: envoy + +HTTP/1.1 200 OK +content-type: application/json +x-content-type-options: nosniff +date: Wed, 08 Feb 2023 02:33:32 GMT +content-length: 460 +x-envoy-upstream-service-time: 2 +server: envoy + +HTTP/1.1 200 OK +content-type: application/json +x-content-type-options: nosniff +date: Wed, 08 Feb 2023 02:33:33 GMT +content-length: 460 +x-envoy-upstream-service-time: 0 +server: envoy + +HTTP/1.1 200 OK +content-type: application/json +x-content-type-options: nosniff +date: Wed, 08 Feb 2023 02:33:33 GMT +content-length: 460 +x-envoy-upstream-service-time: 0 +server: envoy + +``` + ## Rate Limit All Requests This example shows you how to rate limit all requests matching the HTTPRoute rule at 3 requests/Hour by leaving the `clientSelectors` field unset. @@ -809,7 +853,7 @@ spec: targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute - name: http-ratelimit + name: http-ratelimit rateLimit: type: Global global: @@ -858,7 +902,7 @@ spec: targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute - name: http-ratelimit + name: http-ratelimit rateLimit: type: Global global: diff --git a/site/content/en/latest/tasks/traffic/local-rate-limit.md b/site/content/en/latest/tasks/traffic/local-rate-limit.md index 62e369044a5..05caf07258c 100644 --- a/site/content/en/latest/tasks/traffic/local-rate-limit.md +++ b/site/content/en/latest/tasks/traffic/local-rate-limit.md @@ -245,6 +245,227 @@ server: envoy ``` +## Rate Limit Specific User Unless within Test Org + +Here is an example of a rate limit implemented by the application developer to limit a specific user by matching on a custom `x-user-id` header +with a value set to `one`. But the user must not be limited if logging in within Test org, determined by custom header `x-org-id` set to `test`. + +{{< tabpane text=true >}} +{{% tab header="Apply from stdin" %}} + +```shell +cat <}} + +### HTTPRoute + +{{< tabpane text=true >}} +{{% tab header="Apply from stdin" %}} + +```shell +cat <}} + +The HTTPRoute status should indicate that it has been accepted and is bound to the example Gateway. + +```shell +kubectl get httproute/http-ratelimit -o yaml +``` + +Get the Gateway's address: + +```shell +export GATEWAY_HOST=$(kubectl get gateway/eg -o jsonpath='{.status.addresses[0].value}') +``` + +Let's query `ratelimit.example/get` 4 times with `x-user-id` set to `one` and `x-org-id` set to `org1`. We should receive a `200` response from the example Gateway for the first 3 requests and the last request should be rate limited. + +```shell +for i in {1..4}; do curl -I --header "Host: ratelimit.example" --header "x-user-id: one" --header "x-org-id: org1" http://${GATEWAY_HOST}/get ; sleep 1; done +``` + +```console +HTTP/1.1 200 OK +content-type: application/json +x-content-type-options: nosniff +date: Wed, 08 Feb 2023 02:33:31 GMT +content-length: 460 +x-envoy-upstream-service-time: 4 +server: envoy + +HTTP/1.1 200 OK +content-type: application/json +x-content-type-options: nosniff +date: Wed, 08 Feb 2023 02:33:32 GMT +content-length: 460 +x-envoy-upstream-service-time: 2 +server: envoy + +HTTP/1.1 200 OK +content-type: application/json +x-content-type-options: nosniff +date: Wed, 08 Feb 2023 02:33:33 GMT +content-length: 460 +x-envoy-upstream-service-time: 0 +server: envoy + +HTTP/1.1 429 Too Many Requests +x-envoy-ratelimited: true +date: Wed, 08 Feb 2023 02:33:34 GMT +server: envoy +transfer-encoding: chunked + +``` + +Let's query `ratelimit.example/get` 4 times with `x-user-id` set to `one` and `x-org-id` set to `test`. We should receive a `200` response from the example Gateway for all the 4 requests, unlike previous example where the last request was rate limited. + +```shell +for i in {1..4}; do curl -I --header "Host: ratelimit.example" --header "x-user-id: one" --header "x-org-id: test" http://${GATEWAY_HOST}/get ; sleep 1; done +``` + +```console +HTTP/1.1 200 OK +content-type: application/json +x-content-type-options: nosniff +date: Wed, 08 Feb 2023 02:33:31 GMT +content-length: 460 +x-envoy-upstream-service-time: 4 +server: envoy + +HTTP/1.1 200 OK +content-type: application/json +x-content-type-options: nosniff +date: Wed, 08 Feb 2023 02:33:32 GMT +content-length: 460 +x-envoy-upstream-service-time: 2 +server: envoy + +HTTP/1.1 200 OK +content-type: application/json +x-content-type-options: nosniff +date: Wed, 08 Feb 2023 02:33:33 GMT +content-length: 460 +x-envoy-upstream-service-time: 0 +server: envoy + +HTTP/1.1 200 OK +content-type: application/json +x-content-type-options: nosniff +date: Wed, 08 Feb 2023 02:33:33 GMT +content-length: 460 +x-envoy-upstream-service-time: 0 +server: envoy + +``` + ## Rate Limit All Requests This example shows you how to rate limit all requests matching the HTTPRoute rule at 3 requests/Hour by leaving the `clientSelectors` field unset. diff --git a/test/e2e/testdata/ratelimit-header-invert-match.yaml b/test/e2e/testdata/ratelimit-header-invert-match-global.yaml similarity index 100% rename from test/e2e/testdata/ratelimit-header-invert-match.yaml rename to test/e2e/testdata/ratelimit-header-invert-match-global.yaml diff --git a/test/e2e/testdata/ratelimit-header-invert-match-local.yaml b/test/e2e/testdata/ratelimit-header-invert-match-local.yaml new file mode 100644 index 00000000000..0b390af221f --- /dev/null +++ b/test/e2e/testdata/ratelimit-header-invert-match-local.yaml @@ -0,0 +1,41 @@ +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: BackendTrafficPolicy +metadata: + name: ratelimit-specific-user + namespace: gateway-conformance-infra +spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: http-ratelimit-specific-user + rateLimit: + type: Local + local: + rules: + - clientSelectors: + - headers: + - name: x-user-id + value: one + - name: x-org-id + value: test + invert: true + limit: + requests: 3 + unit: Hour +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: http-ratelimit-specific-user + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + rules: + - backendRefs: + - name: infra-backend-v1 + port: 8080 + matches: + - path: + type: Exact + value: /ratelimit-specific-user diff --git a/test/e2e/tests/local_ratelimit.go b/test/e2e/tests/local_ratelimit.go index 4a630d1ed67..f35782de8c0 100644 --- a/test/e2e/tests/local_ratelimit.go +++ b/test/e2e/tests/local_ratelimit.go @@ -25,6 +25,7 @@ func init() { ConformanceTests = append(ConformanceTests, LocalRateLimitSpecificUserTest) ConformanceTests = append(ConformanceTests, LocalRateLimitAllTrafficTest) ConformanceTests = append(ConformanceTests, LocalRateLimitNoLimitRouteTest) + ConformanceTests = append(ConformanceTests, LocalRateLimitHeaderInvertMatchTest) } var LocalRateLimitSpecificUserTest = suite.ConformanceTest{ @@ -207,3 +208,91 @@ var LocalRateLimitNoLimitRouteTest = suite.ConformanceTest{ }) }, } + +var LocalRateLimitHeaderInvertMatchTest = suite.ConformanceTest{ + ShortName: "LocalRateLimitHeaderInvertMatch", + Description: "Limit a specific user unless in a specific org", + Manifests: []string{"testdata/ratelimit-header-invert-match-local.yaml"}, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("limit a specific user", func(t *testing.T) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "http-ratelimit-specific-user", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + + ancestorRef := gwapiv1a2.ParentReference{ + Group: gatewayapi.GroupPtr(gwapiv1.GroupName), + Kind: gatewayapi.KindPtr(resource.KindGateway), + Namespace: gatewayapi.NamespacePtr(gwNN.Namespace), + Name: gwapiv1.ObjectName(gwNN.Name), + } + BackendTrafficPolicyMustBeAccepted(t, suite.Client, types.NamespacedName{Name: "ratelimit-specific-user", Namespace: ns}, suite.ControllerName, ancestorRef) + + expectOkResp := http.ExpectedResponse{ + Request: http.Request{ + Path: "/ratelimit-specific-user", + Headers: map[string]string{ + "x-user-id": "one", + "x-org-id": "org1", + }, + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + + expectOkReq := http.MakeRequest(t, &expectOkResp, gwAddr, "HTTP", "http") + + expectLimitResp := http.ExpectedResponse{ + Request: http.Request{ + Path: "/ratelimit-specific-user", + Headers: map[string]string{ + "x-user-id": "one", + "x-org-id": "org1", + }, + }, + Response: http.Response{ + StatusCode: 429, + }, + Namespace: ns, + } + expectLimitReq := http.MakeRequest(t, &expectLimitResp, gwAddr, "HTTP", "http") + + // should just send exactly 4 requests, and expect 429 + + // keep sending requests till get 200 first, that will cost one 200 + http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectOkResp) + + // fire the rest request + if err := GotExactExpectedResponse(t, 2, suite.RoundTripper, expectOkReq, expectOkResp); err != nil { + t.Errorf("fail to get expected response at first three request: %v", err) + } + + // this request should be limited because the user is one and org is not test and the limit is 3 + if err := GotExactExpectedResponse(t, 1, suite.RoundTripper, expectLimitReq, expectLimitResp); err != nil { + t.Errorf("fail to get expected response at last fourth request: %v", err) + } + + // with test org + expectOkResp = http.ExpectedResponse{ + Request: http.Request{ + Path: "/ratelimit-specific-user", + Headers: map[string]string{ + "x-user-id": "one", + "x-org-id": "test", + }, + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + expectOkReq = http.MakeRequest(t, &expectOkResp, gwAddr, "HTTP", "http") + // the requests should not be limited because the user is one but org is test + if err := GotExactExpectedResponse(t, 4, suite.RoundTripper, expectOkReq, expectOkResp); err != nil { + t.Errorf("fail to get expected response at first three request: %v", err) + } + }) + }, +} diff --git a/test/e2e/tests/ratelimit.go b/test/e2e/tests/ratelimit.go index f0082d77e85..d1e18f74b92 100644 --- a/test/e2e/tests/ratelimit.go +++ b/test/e2e/tests/ratelimit.go @@ -26,7 +26,7 @@ import ( func init() { ConformanceTests = append(ConformanceTests, RateLimitCIDRMatchTest) ConformanceTests = append(ConformanceTests, RateLimitHeaderMatchTest) - ConformanceTests = append(ConformanceTests, RateLimitHeaderInvertMatchTest) + ConformanceTests = append(ConformanceTests, GlobalRateLimitHeaderInvertMatchTest) ConformanceTests = append(ConformanceTests, RateLimitHeadersDisabled) ConformanceTests = append(ConformanceTests, RateLimitBasedJwtClaimsTest) ConformanceTests = append(ConformanceTests, RateLimitMultipleListenersTest) @@ -172,10 +172,10 @@ var RateLimitHeaderMatchTest = suite.ConformanceTest{ }, } -var RateLimitHeaderInvertMatchTest = suite.ConformanceTest{ - ShortName: "RateLimitHeaderInvertMatch", +var GlobalRateLimitHeaderInvertMatchTest = suite.ConformanceTest{ + ShortName: "GlobalRateLimitHeaderInvertMatch", Description: "Limit all requests that match distinct headers except for which invert is set to true", - Manifests: []string{"testdata/ratelimit-header-invert-match.yaml"}, + Manifests: []string{"testdata/ratelimit-header-invert-match-global.yaml"}, Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { ns := "gateway-conformance-infra" routeNN := types.NamespacedName{Name: "header-ratelimit", Namespace: ns} From 544bd9c8b9b1628bd640f3e47f091ee4d62baaf8 Mon Sep 17 00:00:00 2001 From: Huabing Zhao Date: Tue, 22 Oct 2024 20:01:27 +0800 Subject: [PATCH 53/56] feat: implement Backend API for TLSRoute (#4493) add tests for tlsroute with Backend Signed-off-by: Huabing Zhao --- .../testdata/tlsroute-with-backend.out.yaml | 13 +-- internal/gatewayapi/validate.go | 2 +- internal/provider/kubernetes/predicates.go | 20 ++--- test/e2e/base/manifests.yaml | 8 ++ .../testdata/tlsroute-to-backend-fqdn.yaml | 27 ++++++ test/e2e/testdata/tlsroute-to-backend-ip.yaml | 41 +++++++++ ...h_backend.go => httproute_with_backend.go} | 0 test/e2e/tests/tlsroute_with_backend.go | 89 +++++++++++++++++++ 8 files changed, 184 insertions(+), 16 deletions(-) create mode 100644 test/e2e/testdata/tlsroute-to-backend-fqdn.yaml create mode 100644 test/e2e/testdata/tlsroute-to-backend-ip.yaml rename test/e2e/tests/{route_with_backend.go => httproute_with_backend.go} (100%) create mode 100644 test/e2e/tests/tlsroute_with_backend.go diff --git a/internal/gatewayapi/testdata/tlsroute-with-backend.out.yaml b/internal/gatewayapi/testdata/tlsroute-with-backend.out.yaml index 13c3ef41cae..97bce6d0acf 100644 --- a/internal/gatewayapi/testdata/tlsroute-with-backend.out.yaml +++ b/internal/gatewayapi/testdata/tlsroute-with-backend.out.yaml @@ -100,10 +100,9 @@ tlsRoutes: status: "True" type: Accepted - lastTransitionTime: null - message: Resource default/backend-ip of type Backend is not supported for - TLSRoute routes - reason: UnsupportedValue - status: "False" + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" type: ResolvedRefs controllerName: gateway.envoyproxy.io/gatewayclass-controller parentRef: @@ -122,7 +121,11 @@ xdsIR: - destination: name: tlsroute/default/tlsroute-1/rule/-1 settings: - - weight: 1 + - addressType: IP + endpoints: + - host: 1.1.1.1 + port: 3001 + weight: 1 name: tlsroute/default/tlsroute-1 tls: inspector: diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index 6b9a488b86b..fb5d4995700 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -288,7 +288,7 @@ func (t *Translator) validateBackendRefBackend(backendRef *gwapiv1a2.BackendRef, return false } - if kind != resource.KindHTTPRoute { + if kind != resource.KindHTTPRoute && kind != resource.KindTLSRoute { status.SetRouteStatusCondition(routeStatus, parentRef.routeParentStatusIdx, route.GetGeneration(), diff --git a/internal/provider/kubernetes/predicates.go b/internal/provider/kubernetes/predicates.go index a885d58ca62..bfdecbd6e24 100644 --- a/internal/provider/kubernetes/predicates.go +++ b/internal/provider/kubernetes/predicates.go @@ -353,40 +353,40 @@ func (r *gatewayAPIReconciler) isRouteReferencingBackend(nsName *types.Namespace httpRouteList := &gwapiv1.HTTPRouteList{} if err := r.client.List(ctx, httpRouteList, &client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(backendHTTPRouteIndex, nsName.String()), - }); err != nil { - r.log.Error(err, "unable to find associated HTTPRoutes") + }); err != nil && !kerrors.IsNotFound(err) { + r.log.Error(err, "failed to find associated HTTPRoutes") return false } grpcRouteList := &gwapiv1.GRPCRouteList{} if err := r.client.List(ctx, grpcRouteList, &client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(backendGRPCRouteIndex, nsName.String()), - }); err != nil { - r.log.Error(err, "unable to find associated GRPCRoutes") + }); err != nil && !kerrors.IsNotFound(err) { + r.log.Error(err, "failed to find associated GRPCRoutes") return false } tlsRouteList := &gwapiv1a2.TLSRouteList{} if err := r.client.List(ctx, tlsRouteList, &client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(backendTLSRouteIndex, nsName.String()), - }); err != nil { - r.log.Error(err, "unable to find associated TLSRoutes") + }); err != nil && !kerrors.IsNotFound(err) { + r.log.Error(err, "failed to find associated TLSRoutes") return false } tcpRouteList := &gwapiv1a2.TCPRouteList{} if err := r.client.List(ctx, tcpRouteList, &client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(backendTCPRouteIndex, nsName.String()), - }); err != nil { - r.log.Error(err, "unable to find associated TCPRoutes") + }); err != nil && !kerrors.IsNotFound(err) { + r.log.Error(err, "failed to find associated TCPRoutes") return false } udpRouteList := &gwapiv1a2.UDPRouteList{} if err := r.client.List(ctx, udpRouteList, &client.ListOptions{ FieldSelector: fields.OneTermEqualSelector(backendUDPRouteIndex, nsName.String()), - }); err != nil { - r.log.Error(err, "unable to find associated UDPRoutes") + }); err != nil && !kerrors.IsNotFound(err) { + r.log.Error(err, "failed to find associated UDPRoutes") return false } diff --git a/test/e2e/base/manifests.yaml b/test/e2e/base/manifests.yaml index db9a265cba0..714dd296067 100644 --- a/test/e2e/base/manifests.yaml +++ b/test/e2e/base/manifests.yaml @@ -27,6 +27,14 @@ spec: allowedRoutes: namespaces: from: Same + - name: tls + protocol: TLS + port: 443 + tls: + mode: Passthrough + allowedRoutes: + namespaces: + from: Same --- apiVersion: gateway.networking.k8s.io/v1 kind: Gateway diff --git a/test/e2e/testdata/tlsroute-to-backend-fqdn.yaml b/test/e2e/testdata/tlsroute-to-backend-fqdn.yaml new file mode 100644 index 00000000000..cf02dc10eae --- /dev/null +++ b/test/e2e/testdata/tlsroute-to-backend-fqdn.yaml @@ -0,0 +1,27 @@ +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TLSRoute +metadata: + name: tlsroute-to-backend-fqdn + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + sectionName: tls + hostnames: + - "example.com" + rules: + - backendRefs: + - group: gateway.envoyproxy.io + kind: Backend + name: backend-fqdn +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: Backend +metadata: + name: backend-fqdn + namespace: gateway-conformance-infra +spec: + endpoints: + - fqdn: + hostname: tls-backend-2.gateway-conformance-infra.svc.cluster.local + port: 443 diff --git a/test/e2e/testdata/tlsroute-to-backend-ip.yaml b/test/e2e/testdata/tlsroute-to-backend-ip.yaml new file mode 100644 index 00000000000..7206078e2b5 --- /dev/null +++ b/test/e2e/testdata/tlsroute-to-backend-ip.yaml @@ -0,0 +1,41 @@ +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TLSRoute +metadata: + name: tlsroute-to-backend-ip + namespace: gateway-conformance-infra +spec: + parentRefs: + - name: same-namespace + sectionName: tls + hostnames: + - "example.com" + rules: + - backendRefs: + - group: gateway.envoyproxy.io + kind: Backend + name: backend-ip +--- +apiVersion: gateway.envoyproxy.io/v1alpha1 +kind: Backend +metadata: + name: backend-ip + namespace: gateway-conformance-infra +spec: + endpoints: + - ip: + address: 10.96.96.96 + port: 443 +--- +apiVersion: v1 +kind: Service +metadata: + name: tls-backend-2-clusterip + namespace: gateway-conformance-infra +spec: + selector: + app: tls-backend-2 + clusterIP: 10.96.96.96 + ports: + - protocol: TCP + port: 443 + targetPort: 8443 diff --git a/test/e2e/tests/route_with_backend.go b/test/e2e/tests/httproute_with_backend.go similarity index 100% rename from test/e2e/tests/route_with_backend.go rename to test/e2e/tests/httproute_with_backend.go diff --git a/test/e2e/tests/tlsroute_with_backend.go b/test/e2e/tests/tlsroute_with_backend.go new file mode 100644 index 00000000000..c43162ee94f --- /dev/null +++ b/test/e2e/tests/tlsroute_with_backend.go @@ -0,0 +1,89 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +//go:build e2e + +package tests + +import ( + "testing" + + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/gateway-api/conformance/utils/http" + "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" + "sigs.k8s.io/gateway-api/conformance/utils/suite" +) + +func init() { + ConformanceTests = append(ConformanceTests, TLSRouteBackendFQDNTest) + ConformanceTests = append(ConformanceTests, TLSRouteBackendIPTest) +} + +var TLSRouteBackendFQDNTest = suite.ConformanceTest{ + ShortName: "TLSRouteBackendFQDNTest", + Description: "TLSRoutes with a backend ref to a Backend", + Manifests: []string{ + "testdata/tlsroute-to-backend-fqdn.yaml", + }, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("TLSRoute with a FQDN type Backend", func(t *testing.T) { + testTLSRouteWithBackend(t, suite, "tlsroute-to-backend-fqdn", "backend-fqdn") + }) + }, +} + +var TLSRouteBackendIPTest = suite.ConformanceTest{ + ShortName: "TLSRouteBackendIPTest", + Description: "TLSRoutes with a backend ref to a Backend", + Manifests: []string{ + "testdata/tlsroute-to-backend-ip.yaml", + }, + Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { + t.Run("TLSRoute with a IP type Backend", func(t *testing.T) { + testTLSRouteWithBackend(t, suite, "tlsroute-to-backend-ip", "backend-ip") + }) + }, +} + +func testTLSRouteWithBackend(t *testing.T, suite *suite.ConformanceTestSuite, route, backend string) { + ns := "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: route, Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr, _ := kubernetes.GatewayAndTLSRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN, "tls"), routeNN) + certNN := types.NamespacedName{Name: "backend-tls-certificate", Namespace: ns} + + BackendMustBeAccepted(t, suite.Client, types.NamespacedName{Name: backend, Namespace: ns}) + + expected := http.ExpectedResponse{ + Request: http.Request{ + Host: "example.com", + Path: "/", + }, + Response: http.Response{ + StatusCode: 200, + }, + Namespace: ns, + } + + req := http.MakeRequest(t, &expected, gwAddr, "HTTPS", "https") + + // This test uses the same key/cert pair as both a client cert and server cert + // Both backend and client treat the self-signed cert as a trusted CA + cPem, keyPem, err := GetTLSSecret(suite.Client, certNN) + if err != nil { + t.Fatalf("unexpected error finding TLS secret: %v", err) + } + + WaitForConsistentMTLSResponse( + t, + suite.RoundTripper, + req, + expected, + suite.TimeoutConfig.RequiredConsecutiveSuccesses, + suite.TimeoutConfig.MaxTimeToConsistency, + cPem, + keyPem, + "example.com") +} From e24ada61e3f5c294f7c94f707890627b0fc76b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Romain=20B=C3=A9lorgey?= <628135+RomainBelorgey@users.noreply.github.com> Date: Wed, 23 Oct 2024 01:33:33 +0200 Subject: [PATCH 54/56] helm: Remove default cpu limit inside chart (#4290) Remove default cpu limit Signed-off-by: Romain BELORGEY --- charts/gateway-helm/README.md | 1 - charts/gateway-helm/values.tmpl.yaml | 1 - site/content/en/latest/install/gateway-helm-api.md | 1 - site/content/zh/latest/install/gateway-helm-api.md | 1 - test/helm/gateway-helm/certjen-custom-scheduling.out.yaml | 1 - test/helm/gateway-helm/control-plane-with-pdb.out.yaml | 1 - test/helm/gateway-helm/default-config.out.yaml | 1 - test/helm/gateway-helm/deployment-custom-topology.out.yaml | 1 - test/helm/gateway-helm/deployment-images-config.out.yaml | 1 - test/helm/gateway-helm/deployment-priorityclass.out.yaml | 1 - test/helm/gateway-helm/envoy-gateway-config.out.yaml | 1 - test/helm/gateway-helm/global-images-config.out.yaml | 1 - test/helm/gateway-helm/service-annotations.out.yaml | 1 - 13 files changed, 13 deletions(-) diff --git a/charts/gateway-helm/README.md b/charts/gateway-helm/README.md index a352ad78899..61942016a29 100644 --- a/charts/gateway-helm/README.md +++ b/charts/gateway-helm/README.md @@ -68,7 +68,6 @@ To uninstall the chart: | deployment.envoyGateway.image.tag | string | `""` | | | deployment.envoyGateway.imagePullPolicy | string | `""` | | | deployment.envoyGateway.imagePullSecrets | list | `[]` | | -| deployment.envoyGateway.resources.limits.cpu | string | `"500m"` | | | deployment.envoyGateway.resources.limits.memory | string | `"1024Mi"` | | | deployment.envoyGateway.resources.requests.cpu | string | `"100m"` | | | deployment.envoyGateway.resources.requests.memory | string | `"256Mi"` | | diff --git a/charts/gateway-helm/values.tmpl.yaml b/charts/gateway-helm/values.tmpl.yaml index a06f9f232ba..90e72f09956 100644 --- a/charts/gateway-helm/values.tmpl.yaml +++ b/charts/gateway-helm/values.tmpl.yaml @@ -31,7 +31,6 @@ deployment: imagePullSecrets: [] resources: limits: - cpu: 500m memory: 1024Mi requests: cpu: 100m diff --git a/site/content/en/latest/install/gateway-helm-api.md b/site/content/en/latest/install/gateway-helm-api.md index 937a74452ab..99023e65c6c 100644 --- a/site/content/en/latest/install/gateway-helm-api.md +++ b/site/content/en/latest/install/gateway-helm-api.md @@ -32,7 +32,6 @@ The Helm chart for Envoy Gateway | deployment.envoyGateway.image.tag | string | `""` | | | deployment.envoyGateway.imagePullPolicy | string | `""` | | | deployment.envoyGateway.imagePullSecrets | list | `[]` | | -| deployment.envoyGateway.resources.limits.cpu | string | `"500m"` | | | deployment.envoyGateway.resources.limits.memory | string | `"1024Mi"` | | | deployment.envoyGateway.resources.requests.cpu | string | `"100m"` | | | deployment.envoyGateway.resources.requests.memory | string | `"256Mi"` | | diff --git a/site/content/zh/latest/install/gateway-helm-api.md b/site/content/zh/latest/install/gateway-helm-api.md index 937a74452ab..99023e65c6c 100644 --- a/site/content/zh/latest/install/gateway-helm-api.md +++ b/site/content/zh/latest/install/gateway-helm-api.md @@ -32,7 +32,6 @@ The Helm chart for Envoy Gateway | deployment.envoyGateway.image.tag | string | `""` | | | deployment.envoyGateway.imagePullPolicy | string | `""` | | | deployment.envoyGateway.imagePullSecrets | list | `[]` | | -| deployment.envoyGateway.resources.limits.cpu | string | `"500m"` | | | deployment.envoyGateway.resources.limits.memory | string | `"1024Mi"` | | | deployment.envoyGateway.resources.requests.cpu | string | `"100m"` | | | deployment.envoyGateway.resources.requests.memory | string | `"256Mi"` | | diff --git a/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml b/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml index 3746f12a18d..f0c1e0d1309 100644 --- a/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml +++ b/test/helm/gateway-helm/certjen-custom-scheduling.out.yaml @@ -422,7 +422,6 @@ spec: periodSeconds: 10 resources: limits: - cpu: 500m memory: 1024Mi requests: cpu: 100m diff --git a/test/helm/gateway-helm/control-plane-with-pdb.out.yaml b/test/helm/gateway-helm/control-plane-with-pdb.out.yaml index 582fab0e8b9..ab0c09e3ed3 100644 --- a/test/helm/gateway-helm/control-plane-with-pdb.out.yaml +++ b/test/helm/gateway-helm/control-plane-with-pdb.out.yaml @@ -437,7 +437,6 @@ spec: periodSeconds: 10 resources: limits: - cpu: 500m memory: 1024Mi requests: cpu: 100m diff --git a/test/helm/gateway-helm/default-config.out.yaml b/test/helm/gateway-helm/default-config.out.yaml index b4aac21b12c..655c1b7fbeb 100644 --- a/test/helm/gateway-helm/default-config.out.yaml +++ b/test/helm/gateway-helm/default-config.out.yaml @@ -422,7 +422,6 @@ spec: periodSeconds: 10 resources: limits: - cpu: 500m memory: 1024Mi requests: cpu: 100m diff --git a/test/helm/gateway-helm/deployment-custom-topology.out.yaml b/test/helm/gateway-helm/deployment-custom-topology.out.yaml index 37f58022beb..879ca6a2351 100644 --- a/test/helm/gateway-helm/deployment-custom-topology.out.yaml +++ b/test/helm/gateway-helm/deployment-custom-topology.out.yaml @@ -450,7 +450,6 @@ spec: periodSeconds: 10 resources: limits: - cpu: 500m memory: 1024Mi requests: cpu: 100m diff --git a/test/helm/gateway-helm/deployment-images-config.out.yaml b/test/helm/gateway-helm/deployment-images-config.out.yaml index 8506b87b0ee..28eba2f209e 100644 --- a/test/helm/gateway-helm/deployment-images-config.out.yaml +++ b/test/helm/gateway-helm/deployment-images-config.out.yaml @@ -422,7 +422,6 @@ spec: periodSeconds: 10 resources: limits: - cpu: 500m memory: 1024Mi requests: cpu: 100m diff --git a/test/helm/gateway-helm/deployment-priorityclass.out.yaml b/test/helm/gateway-helm/deployment-priorityclass.out.yaml index 3f82746416b..28375ac5bf0 100644 --- a/test/helm/gateway-helm/deployment-priorityclass.out.yaml +++ b/test/helm/gateway-helm/deployment-priorityclass.out.yaml @@ -422,7 +422,6 @@ spec: periodSeconds: 10 resources: limits: - cpu: 500m memory: 1024Mi requests: cpu: 100m diff --git a/test/helm/gateway-helm/envoy-gateway-config.out.yaml b/test/helm/gateway-helm/envoy-gateway-config.out.yaml index 9a3f5b4846b..e401a1062ee 100644 --- a/test/helm/gateway-helm/envoy-gateway-config.out.yaml +++ b/test/helm/gateway-helm/envoy-gateway-config.out.yaml @@ -424,7 +424,6 @@ spec: periodSeconds: 10 resources: limits: - cpu: 500m memory: 1024Mi requests: cpu: 100m diff --git a/test/helm/gateway-helm/global-images-config.out.yaml b/test/helm/gateway-helm/global-images-config.out.yaml index 38be258c7a3..14129b666b6 100644 --- a/test/helm/gateway-helm/global-images-config.out.yaml +++ b/test/helm/gateway-helm/global-images-config.out.yaml @@ -426,7 +426,6 @@ spec: periodSeconds: 10 resources: limits: - cpu: 500m memory: 1024Mi requests: cpu: 100m diff --git a/test/helm/gateway-helm/service-annotations.out.yaml b/test/helm/gateway-helm/service-annotations.out.yaml index 532988e919e..64676e18497 100644 --- a/test/helm/gateway-helm/service-annotations.out.yaml +++ b/test/helm/gateway-helm/service-annotations.out.yaml @@ -424,7 +424,6 @@ spec: periodSeconds: 10 resources: limits: - cpu: 500m memory: 1024Mi requests: cpu: 100m From 337142150fe3aeb0511226c016754e52a58e6f83 Mon Sep 17 00:00:00 2001 From: sh2 Date: Wed, 23 Oct 2024 07:34:55 +0800 Subject: [PATCH 55/56] feat: add host-infra provider support (#4481) * add host-infra provider support Signed-off-by: shawnh2 * add more tests and polish code for each runner Signed-off-by: shawnh2 * fix lint Signed-off-by: shawnh2 --------- Signed-off-by: shawnh2 --- api/v1alpha1/envoygateway_helpers.go | 10 ++ go.mod | 8 +- go.sum | 16 +- internal/cmd/certgen.go | 81 +++++++-- internal/cmd/certgen_test.go | 27 +++ .../translate/out/default-resources.all.yaml | 8 +- internal/crypto/cert_load.go | 57 ++++++ internal/gatewayapi/runner/runner.go | 102 ++++++----- internal/globalratelimit/runner/runner.go | 78 ++++---- internal/infrastructure/common/proxy_args.go | 69 +++++++ internal/infrastructure/common/proxy_sds.go | 27 +++ internal/infrastructure/host/infra.go | 112 ++++++++++++ internal/infrastructure/host/proxy_infra.go | 89 ++++++++++ .../infrastructure/host/proxy_infra_test.go | 89 ++++++++++ .../infrastructure/host/ratelimit_infra.go | 23 +++ .../kubernetes/infra_resource.go | 4 +- .../kubernetes/proxy/resource.go | 85 ++------- .../kubernetes/proxy/resource_provider.go | 17 +- .../proxy/testdata/daemonsets/custom.yaml | 8 +- .../testdata/daemonsets/default-env.yaml | 8 +- .../proxy/testdata/daemonsets/default.yaml | 8 +- .../daemonsets/disable-prometheus.yaml | 8 +- .../testdata/daemonsets/extension-env.yaml | 8 +- .../override-labels-and-annotations.yaml | 8 +- .../testdata/daemonsets/patch-daemonset.yaml | 8 +- .../testdata/daemonsets/shutdown-manager.yaml | 8 +- .../proxy/testdata/daemonsets/volumes.yaml | 8 +- .../testdata/daemonsets/with-annotations.yaml | 8 +- .../testdata/daemonsets/with-extra-args.yaml | 8 +- .../daemonsets/with-image-pull-secrets.yaml | 8 +- .../proxy/testdata/daemonsets/with-name.yaml | 8 +- .../daemonsets/with-node-selector.yaml | 8 +- .../with-topology-spread-constraints.yaml | 8 +- .../proxy/testdata/deployments/custom.yaml | 8 +- .../custom_with_initcontainers.yaml | 8 +- .../testdata/deployments/default-env.yaml | 8 +- .../proxy/testdata/deployments/default.yaml | 8 +- .../deployments/disable-prometheus.yaml | 8 +- .../testdata/deployments/extension-env.yaml | 8 +- .../override-labels-and-annotations.yaml | 8 +- .../deployments/patch-deployment.yaml | 8 +- .../deployments/shutdown-manager.yaml | 8 +- .../proxy/testdata/deployments/volumes.yaml | 8 +- .../deployments/with-annotations.yaml | 8 +- .../deployments/with-empty-memory-limits.yaml | 8 +- .../testdata/deployments/with-extra-args.yaml | 8 +- .../deployments/with-image-pull-secrets.yaml | 8 +- .../proxy/testdata/deployments/with-name.yaml | 8 +- .../deployments/with-node-selector.yaml | 8 +- .../with-topology-spread-constraints.yaml | 8 +- .../kubernetes/proxy_configmap_test.go | 9 +- internal/infrastructure/manager.go | 47 +++-- internal/infrastructure/runner/runner.go | 4 +- internal/utils/file/file.go | 12 ++ internal/utils/file/file_test.go | 28 +++ internal/xds/bootstrap/bootstrap.go | 56 +++++- internal/xds/bootstrap/bootstrap.yaml.tpl | 8 +- internal/xds/bootstrap/bootstrap_test.go | 23 +++ .../testdata/render/custom-server-port.yaml | 168 ++++++++++++++++++ .../testdata/render/custom-stats-matcher.yaml | 8 +- .../testdata/render/disable-prometheus.yaml | 8 +- .../enable-prometheus-gzip-compression.yaml | 8 +- .../testdata/render/enable-prometheus.yaml | 8 +- .../render/otel-metrics-backendref.yaml | 8 +- .../testdata/render/otel-metrics.yaml | 8 +- .../render/with-max-heap-size-bytes.yaml | 8 +- internal/xds/server/runner/runner.go | 78 ++++---- internal/xds/server/runner/runner_test.go | 14 +- 68 files changed, 1227 insertions(+), 434 deletions(-) create mode 100644 internal/crypto/cert_load.go create mode 100644 internal/infrastructure/common/proxy_args.go create mode 100644 internal/infrastructure/common/proxy_sds.go create mode 100644 internal/infrastructure/host/infra.go create mode 100644 internal/infrastructure/host/proxy_infra.go create mode 100644 internal/infrastructure/host/proxy_infra_test.go create mode 100644 internal/infrastructure/host/ratelimit_infra.go create mode 100644 internal/utils/file/file_test.go create mode 100644 internal/xds/bootstrap/testdata/render/custom-server-port.yaml diff --git a/api/v1alpha1/envoygateway_helpers.go b/api/v1alpha1/envoygateway_helpers.go index fed2f6fa075..c61b43c82e1 100644 --- a/api/v1alpha1/envoygateway_helpers.go +++ b/api/v1alpha1/envoygateway_helpers.go @@ -241,6 +241,16 @@ func (r *EnvoyGatewayProvider) GetEnvoyGatewayKubeProvider() *EnvoyGatewayKubern return r.Kubernetes } +func (r *EnvoyGatewayProvider) IsRunningOnKubernetes() bool { + return r.Type == ProviderTypeKubernetes +} + +func (r *EnvoyGatewayProvider) IsRunningOnHost() bool { + return r.Type == ProviderTypeCustom && + r.Custom.Infrastructure != nil && + r.Custom.Infrastructure.Type == InfrastructureProviderTypeHost +} + // DefaultEnvoyGatewayLoggingLevel returns a new EnvoyGatewayLogging with default configuration parameters. // When v1alpha1.LogComponentGatewayDefault specified, all other logging components are ignored. func (logging *EnvoyGatewayLogging) DefaultEnvoyGatewayLoggingLevel(level LogLevel) LogLevel { diff --git a/go.mod b/go.mod index 4d03c682219..1ec72d0a6bd 100644 --- a/go.mod +++ b/go.mod @@ -64,6 +64,7 @@ require ( require ( github.com/docker/docker v27.3.1+incompatible github.com/replicatedhq/troubleshoot v0.107.1 + github.com/tetratelabs/func-e v1.1.5-0.20240822223546-c85a098d5bf0 google.golang.org/grpc v1.67.1 sigs.k8s.io/kubectl-validate v0.0.5-0.20240827210056-ce13d95db263 ) @@ -105,6 +106,7 @@ require ( github.com/containers/storage v1.55.0 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/cyphar/filepath-securejoin v0.3.1 // indirect github.com/distribution/distribution/v3 v3.0.0-beta.1 // indirect github.com/distribution/reference v0.6.0 // indirect @@ -143,7 +145,7 @@ require ( github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect @@ -169,7 +171,7 @@ require ( github.com/ostreedev/ostree-go v0.0.0-20210805093236-719684c64e4f // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rubenv/sql-migrate v1.7.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -190,11 +192,13 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/ulikunitz/xz v0.5.12 // indirect + github.com/urfave/cli/v2 v2.8.1 // indirect github.com/vbatts/tar-split v0.11.5 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.etcd.io/etcd/api/v3 v3.5.14 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect diff --git a/go.sum b/go.sum index d11cb03bbf0..dbaf681efbb 100644 --- a/go.sum +++ b/go.sum @@ -165,6 +165,7 @@ github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfc github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -395,7 +396,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -537,8 +537,8 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9 github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e h1:hz4quJkaJWDo+xW+G6wTF6d6/95QvJ+o2D0+bB/tJ1U= github.com/longhorn/go-iscsi-helper v0.0.0-20210330030558-49a327fb024e/go.mod h1:9z/y9glKmWEdV50tjlUPxFwi1goQfIrrsoZbnMyIZbY= github.com/longhorn/nsfilelock v0.0.0-20200723175406-fa7c83ad0003/go.mod h1:0CLeXlf59Lg6C0kjLSDf47ft73Dh37CwymYRKWwAn04= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY= +github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= github.com/lyft/gostats v0.4.14 h1:xmP4yMfDvEKtlNZEcS2sYz0cvnps1ri337ZEEbw3ab8= github.com/lyft/gostats v0.4.14/go.mod h1:cJWqEVL8JIewIJz/olUIios2F1q06Nc51hXejPQmBH0= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -680,8 +680,8 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= @@ -817,6 +817,8 @@ github.com/telepresenceio/telepresence/rpc/v2 v2.6.8 h1:q5V85LBT9bA/c4YPa/kMvJGy github.com/telepresenceio/telepresence/rpc/v2 v2.6.8/go.mod h1:VlgfRoXaW6Tl8IZbHmMWhITne8HY09/wOFtABHGj3ic= github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7 h1:GMw3nEaOVyi+tNiGko5kAeRtoiEIpXNHmISyZ7fpw14= github.com/telepresenceio/watchable v0.0.0-20220726211108-9bb86f92afa7/go.mod h1:ihJ97e2gsd8GuzFF/I3B1qcik3XZLpXjumQifXi8Slg= +github.com/tetratelabs/func-e v1.1.5-0.20240822223546-c85a098d5bf0 h1:+OHaiOveLnsmUMSZT5vxL6rrpy5rcSsfnx9Mogfo1Kk= +github.com/tetratelabs/func-e v1.1.5-0.20240822223546-c85a098d5bf0/go.mod h1:u78wX1mT5MiSZ3rw8+epQ7fcIT7m83YiwdPT2EWgb0Y= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= @@ -835,6 +837,8 @@ github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljT github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4= +github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= @@ -852,6 +856,8 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= diff --git a/internal/cmd/certgen.go b/internal/cmd/certgen.go index afa626f8d9a..cc9017a17bf 100644 --- a/internal/cmd/certgen.go +++ b/internal/cmd/certgen.go @@ -9,6 +9,7 @@ import ( "context" "errors" "fmt" + "path" "github.com/spf13/cobra" ctrl "sigs.k8s.io/controller-runtime" @@ -19,23 +20,32 @@ import ( "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/provider/kubernetes" + "github.com/envoyproxy/gateway/internal/utils/file" ) +// TODO: make this path configurable or use server config directly. +const defaultLocalCertPath = "/tmp/envoy-gateway/certs" + // getCertGenCommand returns the certGen cobra command to be executed. func getCertGenCommand() *cobra.Command { + var local bool + cmd := &cobra.Command{ Use: "certgen", Short: "Generate Control Plane Certificates", RunE: func(cmd *cobra.Command, args []string) error { - return certGen() + return certGen(local) }, } + cmd.PersistentFlags().BoolVarP(&local, "local", "l", false, + "Generate all the certificates locally.") + return cmd } // certGen generates control plane certificates. -func certGen() error { +func certGen(local bool) error { cfg, err := getConfig() if err != nil { return err @@ -46,22 +56,29 @@ func certGen() error { if err != nil { return fmt.Errorf("failed to generate certificates: %w", err) } - log.Info("generated certificates") - cli, err := client.New(clicfg.GetConfigOrDie(), client.Options{Scheme: envoygateway.GetScheme()}) - if err != nil { - return fmt.Errorf("failed to create controller-runtime client: %w", err) - } + if !local { + log.Info("generated certificates") + cli, err := client.New(clicfg.GetConfigOrDie(), client.Options{Scheme: envoygateway.GetScheme()}) + if err != nil { + return fmt.Errorf("failed to create controller-runtime client: %w", err) + } - if err := outputCerts(ctrl.SetupSignalHandler(), cli, cfg, certs); err != nil { - return fmt.Errorf("failed to output certificates: %w", err) + if err = outputCertsForKubernetes(ctrl.SetupSignalHandler(), cli, cfg, certs); err != nil { + return fmt.Errorf("failed to output certificates: %w", err) + } + } else { + log.Info("generated certificates", "path", defaultLocalCertPath) + if err = outputCertsForLocal(defaultLocalCertPath, certs); err != nil { + return fmt.Errorf("failed to output certificates locally: %w", err) + } } return nil } -// outputCerts outputs the provided certs to a secret in namespace ns. -func outputCerts(ctx context.Context, cli client.Client, cfg *config.Server, certs *crypto.Certificates) error { +// outputCertsForKubernetes outputs the provided certs to a secret in namespace ns. +func outputCertsForKubernetes(ctx context.Context, cli client.Client, cfg *config.Server, certs *crypto.Certificates) error { var updateSecrets bool if cfg.EnvoyGateway != nil && cfg.EnvoyGateway.Provider != nil && @@ -88,3 +105,45 @@ func outputCerts(ctx context.Context, cli client.Client, cfg *config.Server, cer return nil } + +// outputCertsForLocal outputs the provided certs to the local directory as files. +func outputCertsForLocal(localPath string, certs *crypto.Certificates) (err error) { + egDir := path.Join(localPath, "envoy-gateway") + if err = file.WriteDir(certs.CACertificate, egDir, "ca.crt"); err != nil { + return err + } + if err = file.WriteDir(certs.EnvoyGatewayCertificate, egDir, "tls.crt"); err != nil { + return err + } + if err = file.WriteDir(certs.EnvoyGatewayPrivateKey, egDir, "tls.key"); err != nil { + return err + } + + envoyDir := path.Join(localPath, "envoy") + if err = file.WriteDir(certs.CACertificate, envoyDir, "ca.crt"); err != nil { + return err + } + if err = file.WriteDir(certs.EnvoyCertificate, envoyDir, "tls.crt"); err != nil { + return err + } + if err = file.WriteDir(certs.EnvoyPrivateKey, envoyDir, "tls.key"); err != nil { + return err + } + + rlDir := path.Join(localPath, "envoy-rate-limit") + if err = file.WriteDir(certs.CACertificate, rlDir, "ca.crt"); err != nil { + return err + } + if err = file.WriteDir(certs.EnvoyRateLimitCertificate, rlDir, "tls.crt"); err != nil { + return err + } + if err = file.WriteDir(certs.EnvoyRateLimitPrivateKey, rlDir, "tls.key"); err != nil { + return err + } + + if err = file.WriteDir(certs.OIDCHMACSecret, path.Join(localPath, "envoy-oidc-hmac"), "hmac-secret"); err != nil { + return err + } + + return +} diff --git a/internal/cmd/certgen_test.go b/internal/cmd/certgen_test.go index fedd07d2236..299b0e8df23 100644 --- a/internal/cmd/certgen_test.go +++ b/internal/cmd/certgen_test.go @@ -6,12 +6,39 @@ package cmd import ( + "path/filepath" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/envoyproxy/gateway/internal/crypto" ) func TestGetCertgenCommand(t *testing.T) { got := getCertGenCommand() assert.Equal(t, "certgen", got.Use) } + +func TestOutputCertsForLocal(t *testing.T) { + cfg, err := getConfig() + require.NoError(t, err) + + certs, err := crypto.GenerateCerts(cfg) + require.NoError(t, err) + + tmpDir := t.TempDir() + err = outputCertsForLocal(tmpDir, certs) + require.NoError(t, err) + + assert.FileExists(t, filepath.Join(tmpDir, "envoy-gateway", "ca.crt")) + assert.FileExists(t, filepath.Join(tmpDir, "envoy-gateway", "tls.crt")) + assert.FileExists(t, filepath.Join(tmpDir, "envoy-gateway", "tls.key")) + assert.FileExists(t, filepath.Join(tmpDir, "envoy", "ca.crt")) + assert.FileExists(t, filepath.Join(tmpDir, "envoy", "tls.crt")) + assert.FileExists(t, filepath.Join(tmpDir, "envoy", "tls.key")) + assert.FileExists(t, filepath.Join(tmpDir, "envoy-rate-limit", "ca.crt")) + assert.FileExists(t, filepath.Join(tmpDir, "envoy-rate-limit", "tls.crt")) + assert.FileExists(t, filepath.Join(tmpDir, "envoy-rate-limit", "tls.key")) + assert.FileExists(t, filepath.Join(tmpDir, "envoy-oidc-hmac", "hmac-secret")) +} diff --git a/internal/cmd/egctl/testdata/translate/out/default-resources.all.yaml b/internal/cmd/egctl/testdata/translate/out/default-resources.all.yaml index df12da2e2c1..b965d6d9818 100644 --- a/internal/cmd/egctl/testdata/translate/out/default-resources.all.yaml +++ b/internal/cmd/egctl/testdata/translate/out/default-resources.all.yaml @@ -122,13 +122,13 @@ envoyProxyForGatewayClass: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -160,13 +160,13 @@ envoyProxyForGatewayClass: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/crypto/cert_load.go b/internal/crypto/cert_load.go new file mode 100644 index 00000000000..f00dec1d9a3 --- /dev/null +++ b/internal/crypto/cert_load.go @@ -0,0 +1,57 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package crypto + +import ( + "crypto/rand" + "crypto/tls" + "crypto/x509" + "fmt" + "os" +) + +// LoadTLSConfig returns TLSConfig form certificates. +func LoadTLSConfig(tlsCrt, tlsKey, caCrt string) (*tls.Config, error) { + loadConfig := func() (*tls.Config, error) { + cert, err := tls.LoadX509KeyPair(tlsCrt, tlsKey) + if err != nil { + return nil, err + } + + // Load the CA cert. + ca, err := os.ReadFile(caCrt) + if err != nil { + return nil, err + } + + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(ca) { + return nil, fmt.Errorf("failed to parse CA certificate") + } + + return &tls.Config{ + Certificates: []tls.Certificate{cert}, + NextProtos: []string{"h2"}, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: certPool, + MinVersion: tls.VersionTLS13, + }, nil + } + + // Attempt to load certificates and key to catch configuration errors early. + if _, err := loadConfig(); err != nil { + return nil, err + } + + return &tls.Config{ + MinVersion: tls.VersionTLS13, + ClientAuth: tls.RequireAndVerifyClientCert, + Rand: rand.Reader, + GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) { + return loadConfig() + }, + }, nil +} diff --git a/internal/gatewayapi/runner/runner.go b/internal/gatewayapi/runner/runner.go index f8f7b2a6965..bd093761911 100644 --- a/internal/gatewayapi/runner/runner.go +++ b/internal/gatewayapi/runner/runner.go @@ -8,9 +8,7 @@ package runner import ( "context" "crypto/tls" - "crypto/x509" "encoding/json" - "errors" "fmt" "os" "reflect" @@ -27,6 +25,7 @@ import ( gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/crypto" "github.com/envoyproxy/gateway/internal/envoygateway/config" extension "github.com/envoyproxy/gateway/internal/extension/types" "github.com/envoyproxy/gateway/internal/gatewayapi" @@ -37,10 +36,22 @@ import ( ) const ( - wasmCacheDir = "/var/lib/eg/wasm" - serveTLSCertFilename = "/certs/tls.crt" - serveTLSKeyFilename = "/certs/tls.key" - serveTLSCaFilename = "/certs/ca.crt" + wasmCacheDir = "/var/lib/eg/wasm" + + // Default certificates path for envoy-gateway with Kubernetes provider. + serveTLSCertFilepath = "/certs/tls.crt" + serveTLSKeyFilepath = "/certs/tls.key" + serveTLSCaFilepath = "/certs/ca.crt" + + // TODO: Make these path configurable. + // Default certificates path for envoy-gateway with Host infrastructure provider. + localTLSCertFilepath = "/tmp/envoy-gateway/certs/envoy-gateway/tls.crt" + localTLSKeyFilepath = "/tmp/envoy-gateway/certs/envoy-gateway/tls.key" + localTLSCaFilepath = "/tmp/envoy-gateway/certs/envoy-gateway/ca.crt" + + hmacSecretName = "envoy-oidc-hmac" // nolint: gosec + hmacSecretKey = "hmac-secret" + hmacSecretPath = "/tmp/envoy-gateway/certs/envoy-oidc-hmac/hmac-secret" // nolint: gosec ) type Config struct { @@ -62,12 +73,6 @@ func New(cfg *Config) *Runner { } } -const ( - // nolint: gosec - hmacSecretName = "envoy-oidc-hmac" - hmacSecretKey = "hmac-secret" -) - func (r *Runner) Name() string { return string(egv1a1.LogComponentGatewayAPIRunner) } @@ -86,16 +91,12 @@ func (r *Runner) startWasmCache(ctx context.Context) { // Start the wasm cache server // EG reuse the OIDC HMAC secret as a hash salt to generate an unguessable // downloading path for the Wasm module. - salt, err := hmac(ctx, r.Namespace) - if err != nil { - r.Logger.Error(err, "failed to get hmac secret") - return - } - tlsConfig, err := r.tlsConfig() + tlsConfig, salt, err := r.loadTLSConfig(ctx) if err != nil { - r.Logger.Error(err, "failed to create tls config") + r.Logger.Error(err, "failed to start wasm cache") return } + // Create the file directory if it does not exist. if err = fileutils.CreateIfNotExists(wasmCacheDir, true); err != nil { r.Logger.Error(err, "Failed to create Wasm cache directory") @@ -299,6 +300,36 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { r.Logger.Info("shutting down") } +func (r *Runner) loadTLSConfig(ctx context.Context) (tlsConfig *tls.Config, salt []byte, err error) { + switch { + case r.EnvoyGateway.Provider.IsRunningOnKubernetes(): + salt, err = hmac(ctx, r.Namespace) + if err != nil { + return nil, nil, fmt.Errorf("failed to get hmac secret: %w", err) + } + + tlsConfig, err = crypto.LoadTLSConfig(serveTLSCertFilepath, serveTLSKeyFilepath, serveTLSCaFilepath) + if err != nil { + return nil, nil, fmt.Errorf("failed to create tls config: %w", err) + } + + case r.EnvoyGateway.Provider.IsRunningOnHost(): + salt, err = os.ReadFile(hmacSecretPath) + if err != nil { + return nil, nil, fmt.Errorf("failed to get hmac secret: %w", err) + } + + tlsConfig, err = crypto.LoadTLSConfig(localTLSCertFilepath, localTLSKeyFilepath, localTLSCaFilepath) + if err != nil { + return nil, nil, fmt.Errorf("failed to create tls config: %w", err) + } + + default: + return nil, nil, fmt.Errorf("no valid tls certificates") + } + return +} + func unstructuredToPolicyStatus(policyStatus map[string]any) gwapiv1a2.PolicyStatus { var ret gwapiv1a2.PolicyStatus // No need to check the json marshal/unmarshal error, the policyStatus was @@ -537,36 +568,3 @@ func hmac(ctx context.Context, namespace string) (hmac []byte, err error) { } return } - -func (r *Runner) tlsConfig() (*tls.Config, error) { - var ( - serverCert tls.Certificate // server's certificate and private key - caCert []byte // the CA certificate for client verification - caCertPool *x509.CertPool - err error - ) - - // Load server's certificate and private key - if serverCert, err = tls.LoadX509KeyPair(serveTLSCertFilename, serveTLSKeyFilename); err != nil { - return nil, err - } - - // Load client's CA certificate - if caCert, err = os.ReadFile(serveTLSCaFilename); err != nil { - return nil, err - } - - caCertPool = x509.NewCertPool() - if !caCertPool.AppendCertsFromPEM(caCert) { - return nil, errors.New("failed to parse CA certificate") - } - - // Configure the server to require client certificates - return &tls.Config{ - Certificates: []tls.Certificate{serverCert}, - NextProtos: []string{"h2"}, - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: caCertPool, - MinVersion: tls.VersionTLS13, - }, nil -} diff --git a/internal/globalratelimit/runner/runner.go b/internal/globalratelimit/runner/runner.go index baaaaacd6b7..e3430373454 100644 --- a/internal/globalratelimit/runner/runner.go +++ b/internal/globalratelimit/runner/runner.go @@ -7,13 +7,10 @@ package runner import ( "context" - "crypto/rand" "crypto/tls" - "crypto/x509" "fmt" "math" "net" - "os" "strconv" discoveryv3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" @@ -25,6 +22,7 @@ import ( "google.golang.org/grpc/credentials" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/crypto" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/ratelimit" "github.com/envoyproxy/gateway/internal/ir" @@ -36,12 +34,20 @@ import ( const ( // XdsGrpcSotwConfigServerAddress is the listening address of the ratelimit xDS config server. XdsGrpcSotwConfigServerAddress = "0.0.0.0" - // rateLimitTLSCertFilename is the ratelimit tls cert file. - rateLimitTLSCertFilename = "/certs/tls.crt" - // rateLimitTLSKeyFilename is the ratelimit key file. - rateLimitTLSKeyFilename = "/certs/tls.key" - // rateLimitTLSCACertFilename is the ratelimit ca cert file. - rateLimitTLSCACertFilename = "/certs/ca.crt" + + // Default certificates path for envoy-gateway with Kubernetes provider. + // rateLimitTLSCertFilepath is the ratelimit tls cert file. + rateLimitTLSCertFilepath = "/certs/tls.crt" + // rateLimitTLSKeyFilepath is the ratelimit key file. + rateLimitTLSKeyFilepath = "/certs/tls.key" + // rateLimitTLSCACertFilepath is the ratelimit ca cert file. + rateLimitTLSCACertFilepath = "/certs/ca.crt" + + // TODO: Make these path configurable. + // Default certificates path for envoy-gateway with Host infrastructure provider. + localTLSCertFilepath = "/tmp/envoy-gateway/certs/envoy-gateway/tls.crt" + localTLSKeyFilepath = "/tmp/envoy-gateway/certs/envoy-gateway/tls.key" + localTLSCaFilepath = "/tmp/envoy-gateway/certs/envoy-gateway/ca.crt" ) type Config struct { @@ -71,8 +77,13 @@ func (r *Runner) Start(ctx context.Context) (err error) { // Set up the gRPC server and register the xDS handler. // Create SnapshotCache before start subscribeAndTranslate, // prevent panics in case cache is nil. - cfg := r.tlsConfig(rateLimitTLSCertFilename, rateLimitTLSKeyFilename, rateLimitTLSCACertFilename) - r.grpc = grpc.NewServer(grpc.Creds(credentials.NewTLS(cfg))) + tlsConfig, err := r.loadTLSConfig() + if err != nil { + return fmt.Errorf("failed to load TLS config: %w", err) + } + r.Logger.Info("loaded TLS certificate and key") + + r.grpc = grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConfig))) r.cache = cachev3.NewSnapshotCache(false, cachev3.IDHash{}, r.Logger.Sugar()) @@ -193,45 +204,22 @@ func (r *Runner) addNewSnapshot(ctx context.Context, resource types.XdsResources return nil } -func (r *Runner) tlsConfig(cert, key, ca string) *tls.Config { - loadConfig := func() (*tls.Config, error) { - cert, err := tls.LoadX509KeyPair(cert, key) +func (r *Runner) loadTLSConfig() (tlsConfig *tls.Config, err error) { + switch { + case r.EnvoyGateway.Provider.IsRunningOnKubernetes(): + tlsConfig, err = crypto.LoadTLSConfig(rateLimitTLSCertFilepath, rateLimitTLSKeyFilepath, rateLimitTLSCACertFilepath) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create tls config: %w", err) } - // Load the CA cert. - ca, err := os.ReadFile(ca) + case r.EnvoyGateway.Provider.IsRunningOnHost(): + tlsConfig, err = crypto.LoadTLSConfig(localTLSCertFilepath, localTLSKeyFilepath, localTLSCaFilepath) if err != nil { - return nil, err - } - - certPool := x509.NewCertPool() - if !certPool.AppendCertsFromPEM(ca) { - return nil, fmt.Errorf("failed to parse CA certificate") + return nil, fmt.Errorf("failed to create tls config: %w", err) } - return &tls.Config{ - Certificates: []tls.Certificate{cert}, - NextProtos: []string{"h2"}, - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: certPool, - MinVersion: tls.VersionTLS13, - }, nil - } - - // Attempt to load certificates and key to catch configuration errors early. - if _, lerr := loadConfig(); lerr != nil { - r.Logger.Error(lerr, "failed to load certificate and key") - } - r.Logger.Info("loaded TLS certificate and key") - - return &tls.Config{ - MinVersion: tls.VersionTLS13, - ClientAuth: tls.RequireAndVerifyClientCert, - Rand: rand.Reader, - GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) { - return loadConfig() - }, + default: + return nil, fmt.Errorf("no valid tls certificates") } + return } diff --git a/internal/infrastructure/common/proxy_args.go b/internal/infrastructure/common/proxy_args.go new file mode 100644 index 00000000000..0ffaa36c64e --- /dev/null +++ b/internal/infrastructure/common/proxy_args.go @@ -0,0 +1,69 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package common + +import ( + "fmt" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/xds/bootstrap" +) + +// BuildProxyArgs builds command arguments for proxy infrastructure. +func BuildProxyArgs( + infra *ir.ProxyInfra, + shutdownConfig *egv1a1.ShutdownConfig, + bootstrapConfigOptions *bootstrap.RenderBootstrapConfigOptions, + serviceNode string, +) ([]string, error) { + bootstrapConfigurations, err := bootstrap.GetRenderedBootstrapConfig(bootstrapConfigOptions) + if err != nil { + return nil, err + } + + // Apply Bootstrap from EnvoyProxy API if set by the user + // The config should have been validated already. + if infra.Config != nil && infra.Config.Spec.Bootstrap != nil { + bootstrapConfigurations, err = bootstrap.ApplyBootstrapConfig(infra.Config.Spec.Bootstrap, bootstrapConfigurations) + if err != nil { + return nil, err + } + } + + logging := infra.Config.Spec.Logging + + args := []string{ + fmt.Sprintf("--service-cluster %s", infra.Name), + fmt.Sprintf("--service-node %s", serviceNode), + fmt.Sprintf("--config-yaml %s", bootstrapConfigurations), + fmt.Sprintf("--log-level %s", logging.DefaultEnvoyProxyLoggingLevel()), + "--cpuset-threads", + "--drain-strategy immediate", + } + + if infra.Config != nil && + infra.Config.Spec.Concurrency != nil { + args = append(args, fmt.Sprintf("--concurrency %d", *infra.Config.Spec.Concurrency)) + } + + if componentsLogLevel := logging.GetEnvoyProxyComponentLevel(); componentsLogLevel != "" { + args = append(args, fmt.Sprintf("--component-log-level %s", componentsLogLevel)) + } + + // Default drain timeout. + drainTimeout := 60.0 + if shutdownConfig != nil && shutdownConfig.DrainTimeout != nil { + drainTimeout = shutdownConfig.DrainTimeout.Seconds() + } + args = append(args, fmt.Sprintf("--drain-time-s %.0f", drainTimeout)) + + if infra.Config != nil { + args = append(args, infra.Config.Spec.ExtraArgs...) + } + + return args, nil +} diff --git a/internal/infrastructure/common/proxy_sds.go b/internal/infrastructure/common/proxy_sds.go new file mode 100644 index 00000000000..ea6a9227b6e --- /dev/null +++ b/internal/infrastructure/common/proxy_sds.go @@ -0,0 +1,27 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package common + +import "fmt" + +// xDS certificate rotation is supported by using SDS path-based resource files. + +const ( + SdsCAFilename = "xds-trusted-ca.json" + SdsCertFilename = "xds-certificate.json" +) + +func GetSdsCAConfigMapData(ca string) string { + return fmt.Sprintf(`{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",`+ + `"name":"xds_trusted_ca","validation_context":{"trusted_ca":{"filename":"%s"},`+ + `"match_typed_subject_alt_names":[{"san_type":"DNS","matcher":{"exact":"envoy-gateway"}}]}}]}`, ca) +} + +func GetSdsCertConfigMapData(tlsCert, tlsKey string) string { + return fmt.Sprintf(`{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",`+ + `"name":"xds_certificate","tls_certificate":{"certificate_chain":{"filename":"%s"},`+ + `"private_key":{"filename":"%s"}}}]}`, tlsCert, tlsKey) +} diff --git a/internal/infrastructure/host/infra.go b/internal/infrastructure/host/infra.go new file mode 100644 index 00000000000..71804561f3f --- /dev/null +++ b/internal/infrastructure/host/infra.go @@ -0,0 +1,112 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package host + +import ( + "context" + "fmt" + "os" + "path/filepath" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/envoygateway/config" + "github.com/envoyproxy/gateway/internal/infrastructure/common" + "github.com/envoyproxy/gateway/internal/logging" + "github.com/envoyproxy/gateway/internal/utils/file" +) + +const ( + // TODO: Make these path configurable. + defaultHomeDir = "/tmp/envoy-gateway" + defaultLocalCertPathDir = "/tmp/envoy-gateway/certs/envoy" + + // XdsTLSCertFilename is the fully qualified name of the file containing Envoy's + // xDS server TLS certificate. + XdsTLSCertFilename = "tls.crt" + // XdsTLSKeyFilename is the fully qualified name of the file containing Envoy's + // xDS server TLS key. + XdsTLSKeyFilename = "tls.key" + // XdsTLSCaFilename is the fully qualified name of the file containing Envoy's + // trusted CA certificate. + XdsTLSCaFilename = "ca.crt" +) + +// Infra manages the creation and deletion of host process +// based on Infra IR resources. +type Infra struct { + HomeDir string + Logger logging.Logger + + // EnvoyGateway is the configuration used to startup Envoy Gateway. + EnvoyGateway *egv1a1.EnvoyGateway + + // proxyContextMap store the context of each running proxy by its name for lifecycle management. + proxyContextMap map[string]*proxyContext + + // TODO: remove this field once it supports the configurable homeDir + sdsConfigPath string +} + +func NewInfra(runnerCtx context.Context, cfg *config.Server, logger logging.Logger) (*Infra, error) { + // Ensure the home directory exist. + if err := os.MkdirAll(defaultHomeDir, 0o750); err != nil { + return nil, fmt.Errorf("failed to create dir: %w", err) + } + + // Check local certificates dir exist. + if _, err := os.Lstat(defaultLocalCertPathDir); err != nil { + return nil, fmt.Errorf("failed to stat dir: %w", err) + } + + // Ensure the sds config exist. + if err := createSdsConfig(defaultLocalCertPathDir); err != nil { + return nil, fmt.Errorf("failed to create sds config: %w", err) + } + + infra := &Infra{ + HomeDir: defaultHomeDir, + Logger: logger, + EnvoyGateway: cfg.EnvoyGateway, + proxyContextMap: make(map[string]*proxyContext), + sdsConfigPath: defaultLocalCertPathDir, + } + go infra.cleanProxy(runnerCtx) + + return infra, nil +} + +// cleanProxy stops all the running proxies when infra provider is closing. +func (i *Infra) cleanProxy(ctx context.Context) { + <-ctx.Done() + if len(i.proxyContextMap) < 1 { + return + } + + i.Logger.Info("start cleaning up proxies") + for name, proxyCtx := range i.proxyContextMap { + proxyCtx.cancel() + i.Logger.Info("proxy closed", "name", name) + } + i.Logger.Info("all proxies has been cleaned up") +} + +// createSdsConfig creates the needing SDS config under certain directory. +func createSdsConfig(dir string) error { + if err := file.Write(common.GetSdsCAConfigMapData( + filepath.Join(dir, XdsTLSCaFilename)), + filepath.Join(dir, common.SdsCAFilename)); err != nil { + return err + } + + if err := file.Write(common.GetSdsCertConfigMapData( + filepath.Join(dir, XdsTLSCertFilename), + filepath.Join(dir, XdsTLSKeyFilename)), + filepath.Join(dir, common.SdsCertFilename)); err != nil { + return err + } + + return nil +} diff --git a/internal/infrastructure/host/proxy_infra.go b/internal/infrastructure/host/proxy_infra.go new file mode 100644 index 00000000000..371aedc2be9 --- /dev/null +++ b/internal/infrastructure/host/proxy_infra.go @@ -0,0 +1,89 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package host + +import ( + "context" + "errors" + "path/filepath" + + funcE "github.com/tetratelabs/func-e/api" + "k8s.io/utils/ptr" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/infrastructure/common" + "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/utils" + "github.com/envoyproxy/gateway/internal/xds/bootstrap" +) + +type proxyContext struct { + ctx context.Context + cancel context.CancelFunc +} + +// CreateOrUpdateProxyInfra creates the managed host process, if it doesn't exist. +func (i *Infra) CreateOrUpdateProxyInfra(ctx context.Context, infra *ir.Infra) error { + if infra == nil { + return errors.New("infra ir is nil") + } + + if infra.Proxy == nil { + return errors.New("infra proxy ir is nil") + } + + proxyInfra := infra.GetProxyInfra() + proxyName := utils.GetHashedName(proxyInfra.Name, 64) + // Return directly if the proxy is running. + if _, ok := i.proxyContextMap[proxyName]; ok { + return nil + } + + proxyConfig := proxyInfra.GetProxyConfig() + // Disable Prometheus to make envoy running as a host process successfully. + // TODO: Add Prometheus support to host infra. + bootstrapConfigOptions := &bootstrap.RenderBootstrapConfigOptions{ + ProxyMetrics: &egv1a1.ProxyMetrics{ + Prometheus: &egv1a1.ProxyPrometheusProvider{ + Disable: true, + }, + }, + SdsConfig: bootstrap.SdsConfigPath{ + Certificate: filepath.Join(i.sdsConfigPath, common.SdsCertFilename), + TrustedCA: filepath.Join(i.sdsConfigPath, common.SdsCAFilename), + }, + XdsServerHost: ptr.To("0.0.0.0"), + WasmServerPort: ptr.To(int32(0)), + AdminServerPort: ptr.To(int32(0)), + ReadyServerPort: ptr.To(int32(0)), + } + + args, err := common.BuildProxyArgs(proxyInfra, proxyConfig.Spec.Shutdown, bootstrapConfigOptions, proxyName) + if err != nil { + return err + } + + // Create a new context for up-running proxy. + pCtx, cancel := context.WithCancel(context.Background()) + i.proxyContextMap[proxyName] = &proxyContext{ctx: pCtx, cancel: cancel} + return funcE.Run(pCtx, args, funcE.HomeDir(i.HomeDir)) +} + +// DeleteProxyInfra removes the managed host process, if it doesn't exist. +func (i *Infra) DeleteProxyInfra(ctx context.Context, infra *ir.Infra) error { + if infra == nil { + return errors.New("infra ir is nil") + } + + proxyInfra := infra.GetProxyInfra() + proxyName := utils.GetHashedName(proxyInfra.Name, 64) + if pCtx, ok := i.proxyContextMap[proxyName]; ok { + pCtx.cancel() + } + + // Return directly if the proxy is already stopped. + return nil +} diff --git a/internal/infrastructure/host/proxy_infra_test.go b/internal/infrastructure/host/proxy_infra_test.go new file mode 100644 index 00000000000..3437fe5a5d6 --- /dev/null +++ b/internal/infrastructure/host/proxy_infra_test.go @@ -0,0 +1,89 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package host + +import ( + "context" + "path" + "testing" + + "github.com/stretchr/testify/require" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/crypto" + "github.com/envoyproxy/gateway/internal/envoygateway/config" + "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/logging" + "github.com/envoyproxy/gateway/internal/utils/file" +) + +func newMockInfra(t *testing.T, tCtx context.Context, cfg *config.Server, cleanProxy bool) *Infra { + t.Helper() + homeDir := t.TempDir() + // Create envoy certs under home dir. + certs, err := crypto.GenerateCerts(cfg) + require.NoError(t, err) + // Write certs into proxy dir. + proxyDir := path.Join(homeDir, "envoy") + err = file.WriteDir(certs.CACertificate, proxyDir, "ca.crt") + require.NoError(t, err) + err = file.WriteDir(certs.EnvoyCertificate, proxyDir, "tls.crt") + require.NoError(t, err) + err = file.WriteDir(certs.EnvoyPrivateKey, proxyDir, "tls.key") + require.NoError(t, err) + // Write sds config as well. + err = createSdsConfig(proxyDir) + require.NoError(t, err) + + infra := &Infra{ + HomeDir: homeDir, + Logger: logging.DefaultLogger(egv1a1.LogLevelInfo), + EnvoyGateway: cfg.EnvoyGateway, + proxyContextMap: make(map[string]*proxyContext), + sdsConfigPath: proxyDir, + } + if cleanProxy { + go infra.cleanProxy(tCtx) + } + return infra +} + +func TestInfraCreateProxy(t *testing.T) { + cfg, err := config.New() + require.NoError(t, err) + infra := newMockInfra(t, context.Background(), cfg, true) + + // TODO: add more tests once it supports configurable homeDir and runDir. + testCases := []struct { + name string + expect bool + infra *ir.Infra + }{ + { + name: "nil cfg", + expect: false, + infra: nil, + }, + { + name: "nil proxy", + expect: false, + infra: &ir.Infra{ + Proxy: nil, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err = infra.CreateOrUpdateProxyInfra(context.Background(), tc.infra) + if tc.expect { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} diff --git a/internal/infrastructure/host/ratelimit_infra.go b/internal/infrastructure/host/ratelimit_infra.go new file mode 100644 index 00000000000..41871c9137e --- /dev/null +++ b/internal/infrastructure/host/ratelimit_infra.go @@ -0,0 +1,23 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package host + +import ( + "context" + "fmt" +) + +// TODO: add ratelimit support for host infra + +// CreateOrUpdateRateLimitInfra creates the managed host rate limit process, if it doesn't exist. +func (i *Infra) CreateOrUpdateRateLimitInfra(ctx context.Context) error { + return fmt.Errorf("create/update ratelimit infrastructure is not supported yet for host infrastructure") +} + +// DeleteRateLimitInfra removes the managed host rate limit process, if it doesn't exist. +func (i *Infra) DeleteRateLimitInfra(ctx context.Context) error { + return fmt.Errorf("delete ratelimit infrastructure is not supported yet for host infrastructure") +} diff --git a/internal/infrastructure/kubernetes/infra_resource.go b/internal/infrastructure/kubernetes/infra_resource.go index 16cd72b9545..1bec17c0903 100644 --- a/internal/infrastructure/kubernetes/infra_resource.go +++ b/internal/infrastructure/kubernetes/infra_resource.go @@ -17,7 +17,7 @@ import ( "k8s.io/apimachinery/pkg/api/equality" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" + klabels "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -255,7 +255,7 @@ func isSelectorMatch(labelselector *metav1.LabelSelector, l map[string]string) ( return false, fmt.Errorf("invalid label selector is generated: %w", err) } - return selector.Matches(labels.Set(l)), nil + return selector.Matches(klabels.Set(l)), nil } func (i *Infra) createOrUpdatePodDisruptionBudget(ctx context.Context, r ResourceRender) (err error) { diff --git a/internal/infrastructure/kubernetes/proxy/resource.go b/internal/infrastructure/kubernetes/proxy/resource.go index d4d210418b7..6eb8a894e18 100644 --- a/internal/infrastructure/kubernetes/proxy/resource.go +++ b/internal/infrastructure/kubernetes/proxy/resource.go @@ -7,6 +7,7 @@ package proxy import ( "fmt" + "path/filepath" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -15,6 +16,7 @@ import ( egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/cmd/envoy" "github.com/envoyproxy/gateway/internal/envoygateway/config" + "github.com/envoyproxy/gateway/internal/infrastructure/common" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/resource" "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/utils" @@ -22,17 +24,6 @@ import ( ) const ( - SdsCAFilename = "xds-trusted-ca.json" - SdsCertFilename = "xds-certificate.json" - // XdsTLSCertFilename is the fully qualified path of the file containing Envoy's - // xDS server TLS certificate. - XdsTLSCertFilename = "/certs/tls.crt" - // XdsTLSKeyFilename is the fully qualified path of the file containing Envoy's - // xDS server TLS key. - XdsTLSKeyFilename = "/certs/tls.key" - // XdsTLSCaFilename is the fully qualified path of the file containing Envoy's - // trusted CA certificate. - XdsTLSCaFilename = "/certs/ca.crt" // envoyContainerName is the name of the Envoy container. envoyContainerName = "envoy" // envoyNsEnvVar is the name of the Envoy Gateway namespace environment variable. @@ -41,16 +32,6 @@ const ( envoyPodEnvVar = "ENVOY_POD_NAME" ) -var ( - // xDS certificate rotation is supported by using SDS path-based resource files. - SdsCAConfigMapData = fmt.Sprintf(`{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",`+ - `"name":"xds_trusted_ca","validation_context":{"trusted_ca":{"filename":"%s"},`+ - `"match_typed_subject_alt_names":[{"san_type":"DNS","matcher":{"exact":"envoy-gateway"}}]}}]}`, XdsTLSCaFilename) - SdsCertConfigMapData = fmt.Sprintf(`{"resources":[{"@type":"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",`+ - `"name":"xds_certificate","tls_certificate":{"certificate_chain":{"filename":"%s"},`+ - `"private_key":{"filename":"%s"}}}]}`, XdsTLSCertFilename, XdsTLSKeyFilename) -) - // ExpectedResourceHashedName returns expected resource hashed name including up to the 48 characters of the original name. func ExpectedResourceHashedName(name string) string { hashedName := utils.GetHashedName(name, 48) @@ -135,8 +116,6 @@ func expectedProxyContainers(infra *ir.ProxyInfra, }) } - var bootstrapConfigurations string - var proxyMetrics *egv1a1.ProxyMetrics if infra.Config != nil && infra.Config.Spec.Telemetry != nil { @@ -146,52 +125,18 @@ func expectedProxyContainers(infra *ir.ProxyInfra, maxHeapSizeBytes := calculateMaxHeapSizeBytes(containerSpec.Resources) // Get the default Bootstrap - bootstrapConfigurations, err := bootstrap.GetRenderedBootstrapConfig(&bootstrap.RenderBootstrapConfigOptions{ - ProxyMetrics: proxyMetrics, + bootstrapConfigOptions := &bootstrap.RenderBootstrapConfigOptions{ + ProxyMetrics: proxyMetrics, + SdsConfig: bootstrap.SdsConfigPath{ + Certificate: filepath.Join("/sds", common.SdsCertFilename), + TrustedCA: filepath.Join("/sds", common.SdsCAFilename), + }, MaxHeapSizeBytes: maxHeapSizeBytes, - }) - if err != nil { - return nil, err } - // Apply Bootstrap from EnvoyProxy API if set by the user - // The config should have been validated already - if infra.Config != nil && infra.Config.Spec.Bootstrap != nil { - bootstrapConfigurations, err = bootstrap.ApplyBootstrapConfig(infra.Config.Spec.Bootstrap, bootstrapConfigurations) - if err != nil { - return nil, err - } - } - - logging := infra.Config.Spec.Logging - - args := []string{ - fmt.Sprintf("--service-cluster %s", infra.Name), - fmt.Sprintf("--service-node $(%s)", envoyPodEnvVar), - fmt.Sprintf("--config-yaml %s", bootstrapConfigurations), - fmt.Sprintf("--log-level %s", logging.DefaultEnvoyProxyLoggingLevel()), - "--cpuset-threads", - "--drain-strategy immediate", - } - - if infra.Config != nil && - infra.Config.Spec.Concurrency != nil { - args = append(args, fmt.Sprintf("--concurrency %d", *infra.Config.Spec.Concurrency)) - } - - if componentsLogLevel := logging.GetEnvoyProxyComponentLevel(); componentsLogLevel != "" { - args = append(args, fmt.Sprintf("--component-log-level %s", componentsLogLevel)) - } - - // Default - drainTimeout := 60.0 - if shutdownConfig != nil && shutdownConfig.DrainTimeout != nil { - drainTimeout = shutdownConfig.DrainTimeout.Seconds() - } - args = append(args, fmt.Sprintf("--drain-time-s %.0f", drainTimeout)) - - if infra.Config != nil { - args = append(args, infra.Config.Spec.ExtraArgs...) + args, err := common.BuildProxyArgs(infra, shutdownConfig, bootstrapConfigOptions, fmt.Sprintf("$(%s)", envoyPodEnvVar)) + if err != nil { + return nil, err } containers := []corev1.Container{ @@ -378,12 +323,12 @@ func expectedVolumes(name string, pod *egv1a1.KubernetesPodSpec) []corev1.Volume }, Items: []corev1.KeyToPath{ { - Key: SdsCAFilename, - Path: SdsCAFilename, + Key: common.SdsCAFilename, + Path: common.SdsCAFilename, }, { - Key: SdsCertFilename, - Path: SdsCertFilename, + Key: common.SdsCertFilename, + Path: common.SdsCertFilename, }, }, DefaultMode: ptr.To[int32](420), diff --git a/internal/infrastructure/kubernetes/proxy/resource_provider.go b/internal/infrastructure/kubernetes/proxy/resource_provider.go index 5411c29e1c6..b1d47e906d9 100644 --- a/internal/infrastructure/kubernetes/proxy/resource_provider.go +++ b/internal/infrastructure/kubernetes/proxy/resource_provider.go @@ -21,11 +21,24 @@ import ( egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/gatewayapi" + "github.com/envoyproxy/gateway/internal/infrastructure/common" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/resource" "github.com/envoyproxy/gateway/internal/ir" "github.com/envoyproxy/gateway/internal/xds/bootstrap" ) +const ( + // XdsTLSCertFilepath is the fully qualified path of the file containing Envoy's + // xDS server TLS certificate. + XdsTLSCertFilepath = "/certs/tls.crt" + // XdsTLSKeyFilepath is the fully qualified path of the file containing Envoy's + // xDS server TLS key. + XdsTLSKeyFilepath = "/certs/tls.key" + // XdsTLSCaFilepath is the fully qualified path of the file containing Envoy's + // trusted CA certificate. + XdsTLSCaFilepath = "/certs/ca.crt" +) + type ResourceRender struct { infra *ir.ProxyInfra @@ -201,8 +214,8 @@ func (r *ResourceRender) ConfigMap() (*corev1.ConfigMap, error) { Annotations: r.infra.GetProxyMetadata().Annotations, }, Data: map[string]string{ - SdsCAFilename: SdsCAConfigMapData, - SdsCertFilename: SdsCertConfigMapData, + common.SdsCAFilename: common.GetSdsCAConfigMapData(XdsTLSCaFilepath), + common.SdsCertFilename: common.GetSdsCertConfigMapData(XdsTLSCertFilepath, XdsTLSKeyFilepath), }, }, nil } diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/custom.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/custom.yaml index a3bad89951a..678eebb7cac 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/custom.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/custom.yaml @@ -154,13 +154,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -192,13 +192,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/default-env.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/default-env.yaml index 6afaa48eb50..4cc285a5ea4 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/default-env.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/default-env.yaml @@ -153,13 +153,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -191,13 +191,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/default.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/default.yaml index c55efd9a651..26fcec34835 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/default.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/default.yaml @@ -153,13 +153,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -191,13 +191,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/disable-prometheus.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/disable-prometheus.yaml index 0190a032347..a6a402450e3 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/disable-prometheus.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/disable-prometheus.yaml @@ -127,13 +127,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -165,13 +165,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/extension-env.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/extension-env.yaml index 59abefc9f7e..6642390520f 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/extension-env.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/extension-env.yaml @@ -153,13 +153,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -191,13 +191,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/override-labels-and-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/override-labels-and-annotations.yaml index acf981d6eaf..3fa8846e2fb 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/override-labels-and-annotations.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/override-labels-and-annotations.yaml @@ -162,13 +162,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -200,13 +200,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/patch-daemonset.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/patch-daemonset.yaml index 6eaf679f35b..e673031325d 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/patch-daemonset.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/patch-daemonset.yaml @@ -153,13 +153,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -191,13 +191,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/shutdown-manager.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/shutdown-manager.yaml index b4ff0f41c3b..2604c99141f 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/shutdown-manager.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/shutdown-manager.yaml @@ -153,13 +153,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -191,13 +191,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/volumes.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/volumes.yaml index 8707cd1289b..ee1d74b0f16 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/volumes.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/volumes.yaml @@ -153,13 +153,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -191,13 +191,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-annotations.yaml index 1288195935a..5d862446c38 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-annotations.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-annotations.yaml @@ -158,13 +158,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -196,13 +196,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-extra-args.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-extra-args.yaml index c4e4ef57509..a2ba2172f18 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-extra-args.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-extra-args.yaml @@ -153,13 +153,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -191,13 +191,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-image-pull-secrets.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-image-pull-secrets.yaml index a368ec3334e..75451d36eca 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-image-pull-secrets.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-image-pull-secrets.yaml @@ -153,13 +153,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -191,13 +191,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-name.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-name.yaml index 34d8438df3d..564842ad1df 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-name.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-name.yaml @@ -153,13 +153,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -191,13 +191,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-node-selector.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-node-selector.yaml index aa6aba8b813..6e3f9b20f94 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-node-selector.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-node-selector.yaml @@ -153,13 +153,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -191,13 +191,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-topology-spread-constraints.yaml b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-topology-spread-constraints.yaml index a69ed0181ee..341bbd81f8a 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-topology-spread-constraints.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/daemonsets/with-topology-spread-constraints.yaml @@ -153,13 +153,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -191,13 +191,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom.yaml index 7825fe6a5d4..31841738dee 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom.yaml @@ -159,13 +159,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -197,13 +197,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom_with_initcontainers.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom_with_initcontainers.yaml index 7c19b8404b4..a81f3c8335a 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom_with_initcontainers.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/custom_with_initcontainers.yaml @@ -159,13 +159,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -197,13 +197,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/default-env.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/default-env.yaml index 608be7b1164..d90e6910a18 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/default-env.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/default-env.yaml @@ -158,13 +158,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -196,13 +196,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml index 7ab09dccee7..4a1d7251b9e 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/default.yaml @@ -157,13 +157,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -195,13 +195,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/disable-prometheus.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/disable-prometheus.yaml index 8eba24a439e..8a9f46f5bd8 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/disable-prometheus.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/disable-prometheus.yaml @@ -131,13 +131,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -169,13 +169,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/extension-env.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/extension-env.yaml index 2cb179ea5ce..53220f06d29 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/extension-env.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/extension-env.yaml @@ -158,13 +158,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -196,13 +196,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml index a2ad5c78026..f8247753baf 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/override-labels-and-annotations.yaml @@ -166,13 +166,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -204,13 +204,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/patch-deployment.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/patch-deployment.yaml index 7e742ac47f0..10452128cec 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/patch-deployment.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/patch-deployment.yaml @@ -157,13 +157,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -195,13 +195,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/shutdown-manager.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/shutdown-manager.yaml index 9abe8a6cae2..611f1f9eb06 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/shutdown-manager.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/shutdown-manager.yaml @@ -157,13 +157,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -195,13 +195,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/volumes.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/volumes.yaml index b5fca7f2c29..62deebaba1e 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/volumes.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/volumes.yaml @@ -158,13 +158,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -196,13 +196,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml index c96b3d7a3da..e4d4eac6563 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-annotations.yaml @@ -162,13 +162,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -200,13 +200,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-empty-memory-limits.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-empty-memory-limits.yaml index 001c2c627be..b24dfaea2a4 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-empty-memory-limits.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-empty-memory-limits.yaml @@ -157,13 +157,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -195,13 +195,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-extra-args.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-extra-args.yaml index 3a44833a244..b41a5135a58 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-extra-args.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-extra-args.yaml @@ -157,13 +157,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -195,13 +195,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-image-pull-secrets.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-image-pull-secrets.yaml index ba938efc580..bb79e14f0e4 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-image-pull-secrets.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-image-pull-secrets.yaml @@ -157,13 +157,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -195,13 +195,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-name.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-name.yaml index 3b4404c54e4..1f9239bfb3d 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-name.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-name.yaml @@ -157,13 +157,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -195,13 +195,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-node-selector.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-node-selector.yaml index f8288066623..b6845764655 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-node-selector.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-node-selector.yaml @@ -157,13 +157,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -195,13 +195,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-topology-spread-constraints.yaml b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-topology-spread-constraints.yaml index 1b3a530ce53..0436bc70075 100644 --- a/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-topology-spread-constraints.yaml +++ b/internal/infrastructure/kubernetes/proxy/testdata/deployments/with-topology-spread-constraints.yaml @@ -157,13 +157,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -195,13 +195,13 @@ spec: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/infrastructure/kubernetes/proxy_configmap_test.go b/internal/infrastructure/kubernetes/proxy_configmap_test.go index a761b569498..ef461fdde0a 100644 --- a/internal/infrastructure/kubernetes/proxy_configmap_test.go +++ b/internal/infrastructure/kubernetes/proxy_configmap_test.go @@ -20,6 +20,7 @@ import ( "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/gatewayapi" + "github.com/envoyproxy/gateway/internal/infrastructure/common" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes/proxy" "github.com/envoyproxy/gateway/internal/ir" ) @@ -53,8 +54,8 @@ func TestCreateOrUpdateProxyConfigMap(t *testing.T) { }, }, Data: map[string]string{ - proxy.SdsCAFilename: proxy.SdsCAConfigMapData, - proxy.SdsCertFilename: proxy.SdsCertConfigMapData, + common.SdsCAFilename: common.GetSdsCAConfigMapData(proxy.XdsTLSCaFilepath), + common.SdsCertFilename: common.GetSdsCertConfigMapData(proxy.XdsTLSCertFilepath, proxy.XdsTLSKeyFilepath), }, }, }, @@ -87,8 +88,8 @@ func TestCreateOrUpdateProxyConfigMap(t *testing.T) { }, }, Data: map[string]string{ - proxy.SdsCAFilename: proxy.SdsCAConfigMapData, - proxy.SdsCertFilename: proxy.SdsCertConfigMapData, + common.SdsCAFilename: common.GetSdsCAConfigMapData(proxy.XdsTLSCaFilepath), + common.SdsCertFilename: common.GetSdsCertConfigMapData(proxy.XdsTLSCertFilepath, proxy.XdsTLSKeyFilepath), }, }, }, diff --git a/internal/infrastructure/manager.go b/internal/infrastructure/manager.go index 198acef8708..0254eb1853b 100644 --- a/internal/infrastructure/manager.go +++ b/internal/infrastructure/manager.go @@ -15,11 +15,16 @@ import ( egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" "github.com/envoyproxy/gateway/internal/envoygateway" "github.com/envoyproxy/gateway/internal/envoygateway/config" + "github.com/envoyproxy/gateway/internal/infrastructure/host" "github.com/envoyproxy/gateway/internal/infrastructure/kubernetes" "github.com/envoyproxy/gateway/internal/ir" + "github.com/envoyproxy/gateway/internal/logging" ) -var _ Manager = (*kubernetes.Infra)(nil) +var ( + _ Manager = (*kubernetes.Infra)(nil) + _ Manager = (*host.Infra)(nil) +) // Manager provides the scaffolding for managing infrastructure. type Manager interface { @@ -34,26 +39,34 @@ type Manager interface { } // NewManager returns a new infrastructure Manager. -func NewManager(cfg *config.Server) (Manager, error) { - var mgr Manager - +func NewManager(ctx context.Context, cfg *config.Server, logger logging.Logger) (mgr Manager, err error) { switch cfg.EnvoyGateway.Provider.Type { case egv1a1.ProviderTypeKubernetes: - cli, err := client.New(clicfg.GetConfigOrDie(), client.Options{Scheme: envoygateway.GetScheme()}) - if err != nil { - return nil, err - } - mgr = kubernetes.NewInfra(cli, cfg) + mgr, err = newManagerForKubernetes(cfg) case egv1a1.ProviderTypeCustom: - infra := cfg.EnvoyGateway.Provider.Custom.Infrastructure - switch infra.Type { - case egv1a1.InfrastructureProviderTypeHost: - // TODO(sh2): implement host provider - return nil, fmt.Errorf("host provider is not available yet") - default: - return nil, fmt.Errorf("unsupported provider type: %s", infra.Type) - } + mgr, err = newManagerForCustom(ctx, cfg, logger) } + if err != nil { + return nil, err + } return mgr, nil } + +func newManagerForKubernetes(cfg *config.Server) (Manager, error) { + cli, err := client.New(clicfg.GetConfigOrDie(), client.Options{Scheme: envoygateway.GetScheme()}) + if err != nil { + return nil, err + } + return kubernetes.NewInfra(cli, cfg), nil +} + +func newManagerForCustom(ctx context.Context, cfg *config.Server, logger logging.Logger) (Manager, error) { + infra := cfg.EnvoyGateway.Provider.Custom.Infrastructure + switch infra.Type { + case egv1a1.InfrastructureProviderTypeHost: + return host.NewInfra(ctx, cfg, logger) + default: + return nil, fmt.Errorf("unsupported provider type: %s", infra.Type) + } +} diff --git a/internal/infrastructure/runner/runner.go b/internal/infrastructure/runner/runner.go index 300314d92e0..6896a6e5a16 100644 --- a/internal/infrastructure/runner/runner.go +++ b/internal/infrastructure/runner/runner.go @@ -40,11 +40,11 @@ func (r *Runner) Start(ctx context.Context) (err error) { r.Logger = r.Logger.WithName(r.Name()).WithValues("runner", r.Name()) if r.EnvoyGateway.Provider.Type == egv1a1.ProviderTypeCustom && r.EnvoyGateway.Provider.Custom.Infrastructure == nil { - r.Logger.Info("provider is not specified, no provider is available") + r.Logger.Info("provider is not specified, no infrastructure is available") return nil } - r.mgr, err = infrastructure.NewManager(&r.Config.Server) + r.mgr, err = infrastructure.NewManager(ctx, &r.Config.Server, r.Logger) if err != nil { r.Logger.Error(err, "failed to create new manager") return err diff --git a/internal/utils/file/file.go b/internal/utils/file/file.go index 51a02571493..da4cb67a877 100644 --- a/internal/utils/file/file.go +++ b/internal/utils/file/file.go @@ -8,8 +8,10 @@ package file import ( "bufio" "os" + "path/filepath" ) +// Write writes data into a given filepath. func Write(data string, filepath string) error { file, err := os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644) if err != nil { @@ -25,3 +27,13 @@ func Write(data string, filepath string) error { return nil } + +// WriteDir write data into a given filename under certain directory. +func WriteDir(data []byte, dir, filename string) error { + err := os.MkdirAll(dir, 0o755) + if err != nil { + return err + } + + return Write(string(data), filepath.Join(dir, filename)) +} diff --git a/internal/utils/file/file_test.go b/internal/utils/file/file_test.go new file mode 100644 index 00000000000..488ff415191 --- /dev/null +++ b/internal/utils/file/file_test.go @@ -0,0 +1,28 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package file + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestWriteDir(t *testing.T) { + tmpDir := t.TempDir() + testFilename := "test" + data := []byte("foobar") + + err := WriteDir(data, tmpDir, testFilename) + require.NoError(t, err) + require.FileExists(t, filepath.Join(tmpDir, testFilename)) + + got, err := os.ReadFile(filepath.Join(tmpDir, testFilename)) + require.NoError(t, err) + require.Equal(t, data, got) +} diff --git a/internal/xds/bootstrap/bootstrap.go b/internal/xds/bootstrap/bootstrap.go index e74620483f4..0efad8c314f 100644 --- a/internal/xds/bootstrap/bootstrap.go +++ b/internal/xds/bootstrap/bootstrap.go @@ -42,6 +42,9 @@ const ( envoyReadinessAddress = "0.0.0.0" EnvoyReadinessPort = 19001 EnvoyReadinessPath = "/ready" + + defaultSdsTrustedCAPath = "/sds/xds-trusted-ca.json" + defaultSdsCertificatePath = "/sds/xds-certificate.json" ) //go:embed bootstrap.yaml.tpl @@ -67,6 +70,12 @@ type bootstrapParameters struct { AdminServer adminServerParameters // ReadyServer defines the configuration for health check ready listener ReadyServer readyServerParameters + + // SdsCertificatePath defines the path to SDS certificate config. + SdsCertificatePath string + // SdsTrustedCAPath defines the path to SDS trusted CA config. + SdsTrustedCAPath string + // EnablePrometheus defines whether to enable metrics endpoint for prometheus. EnablePrometheus bool // EnablePrometheusCompression defines whether to enable HTTP compression on metrics endpoint for prometheus. @@ -130,9 +139,20 @@ type overloadManagerParameters struct { type RenderBootstrapConfigOptions struct { ProxyMetrics *egv1a1.ProxyMetrics + SdsConfig SdsConfigPath + XdsServerHost *string + XdsServerPort *int32 + WasmServerPort *int32 + AdminServerPort *int32 + ReadyServerPort *int32 MaxHeapSizeBytes uint64 } +type SdsConfigPath struct { + Certificate string + TrustedCA string +} + // render the stringified bootstrap config in yaml format. func (b *bootstrapConfig) render() error { buf := new(strings.Builder) @@ -238,17 +258,47 @@ func GetRenderedBootstrapConfig(opts *RenderBootstrapConfigOptions) (string, err Port: EnvoyReadinessPort, ReadinessPath: EnvoyReadinessPath, }, + SdsCertificatePath: defaultSdsCertificatePath, + SdsTrustedCAPath: defaultSdsTrustedCAPath, EnablePrometheus: enablePrometheus, EnablePrometheusCompression: enablePrometheusCompression, PrometheusCompressionLibrary: PrometheusCompressionLibrary, OtelMetricSinks: metricSinks, }, } - if opts != nil && opts.ProxyMetrics != nil && opts.ProxyMetrics.Matches != nil { - cfg.parameters.StatsMatcher = &StatsMatcher - } + // Bootstrap config override if opts != nil { + if opts.ProxyMetrics != nil && opts.ProxyMetrics.Matches != nil { + cfg.parameters.StatsMatcher = &StatsMatcher + } + + // Override Sds configs + if len(opts.SdsConfig.Certificate) > 0 { + cfg.parameters.SdsCertificatePath = opts.SdsConfig.Certificate + } + if len(opts.SdsConfig.TrustedCA) > 0 { + cfg.parameters.SdsTrustedCAPath = opts.SdsConfig.TrustedCA + } + + if opts.XdsServerHost != nil { + cfg.parameters.XdsServer.Address = *opts.XdsServerHost + } + + // Override the various server port + if opts.XdsServerPort != nil { + cfg.parameters.XdsServer.Port = *opts.XdsServerPort + } + if opts.AdminServerPort != nil { + cfg.parameters.AdminServer.Port = *opts.AdminServerPort + } + if opts.ReadyServerPort != nil { + cfg.parameters.ReadyServer.Port = *opts.ReadyServerPort + } + if opts.WasmServerPort != nil { + cfg.parameters.WasmServer.Port = *opts.WasmServerPort + } + cfg.parameters.OverloadManager.MaxHeapSizeBytes = opts.MaxHeapSizeBytes } diff --git a/internal/xds/bootstrap/bootstrap.yaml.tpl b/internal/xds/bootstrap/bootstrap.yaml.tpl index b7d26c7d4a9..d243b7777ec 100644 --- a/internal/xds/bootstrap/bootstrap.yaml.tpl +++ b/internal/xds/bootstrap/bootstrap.yaml.tpl @@ -191,13 +191,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: {{ .SdsCertificatePath }} resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: {{ .SdsTrustedCAPath }} resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -229,13 +229,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: {{ .SdsCertificatePath }} resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: {{ .SdsTrustedCAPath }} resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/xds/bootstrap/bootstrap_test.go b/internal/xds/bootstrap/bootstrap_test.go index 2023a7096bd..3c334eeaeb5 100644 --- a/internal/xds/bootstrap/bootstrap_test.go +++ b/internal/xds/bootstrap/bootstrap_test.go @@ -20,6 +20,11 @@ import ( ) func TestGetRenderedBootstrapConfig(t *testing.T) { + sds := SdsConfigPath{ + Certificate: "/sds/xds-certificate.json", + TrustedCA: "/sds/xds-trusted-ca.json", + } + cases := []struct { name string opts *RenderBootstrapConfigOptions @@ -32,6 +37,7 @@ func TestGetRenderedBootstrapConfig(t *testing.T) { Disable: true, }, }, + SdsConfig: sds, }, }, { @@ -40,6 +46,7 @@ func TestGetRenderedBootstrapConfig(t *testing.T) { ProxyMetrics: &egv1a1.ProxyMetrics{ Prometheus: &egv1a1.ProxyPrometheusProvider{}, }, + SdsConfig: sds, }, }, { @@ -52,6 +59,7 @@ func TestGetRenderedBootstrapConfig(t *testing.T) { }, }, }, + SdsConfig: sds, }, }, { @@ -71,6 +79,7 @@ func TestGetRenderedBootstrapConfig(t *testing.T) { }, }, }, + SdsConfig: sds, }, }, { @@ -101,6 +110,7 @@ func TestGetRenderedBootstrapConfig(t *testing.T) { }, }, }, + SdsConfig: sds, }, }, { @@ -130,12 +140,25 @@ func TestGetRenderedBootstrapConfig(t *testing.T) { }, }, }, + SdsConfig: sds, + }, + }, + { + name: "custom-server-port", + opts: &RenderBootstrapConfigOptions{ + XdsServerHost: ptr.To("foo.bar"), + XdsServerPort: ptr.To(int32(12345)), + WasmServerPort: ptr.To(int32(1111)), + AdminServerPort: ptr.To(int32(2222)), + ReadyServerPort: ptr.To(int32(3333)), + SdsConfig: sds, }, }, { name: "with-max-heap-size-bytes", opts: &RenderBootstrapConfigOptions{ MaxHeapSizeBytes: 1073741824, + SdsConfig: sds, }, }, } diff --git a/internal/xds/bootstrap/testdata/render/custom-server-port.yaml b/internal/xds/bootstrap/testdata/render/custom-server-port.yaml new file mode 100644 index 00000000000..23cd059a2a6 --- /dev/null +++ b/internal/xds/bootstrap/testdata/render/custom-server-port.yaml @@ -0,0 +1,168 @@ +admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: /dev/null + address: + socket_address: + address: 127.0.0.1 + port_value: 2222 +layered_runtime: + layers: + - name: global_config + static_layer: + envoy.restart_features.use_eds_cache_for_ads: true + re2.max_program_size.error_level: 4294967295 + re2.max_program_size.warn_level: 1000 +dynamic_resources: + ads_config: + api_type: DELTA_GRPC + transport_api_version: V3 + grpc_services: + - envoy_grpc: + cluster_name: xds_cluster + set_node_on_first_message_only: true + lds_config: + ads: {} + resource_api_version: V3 + cds_config: + ads: {} + resource_api_version: V3 +static_resources: + listeners: + - name: envoy-gateway-proxy-ready-0.0.0.0-3333 + address: + socket_address: + address: 0.0.0.0 + port_value: 3333 + protocol: TCP + filter_chains: + - filters: + - name: envoy.filters.network.http_connection_manager + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + stat_prefix: eg-ready-http + route_config: + name: local_route + virtual_hosts: + - name: prometheus_stats + domains: + - "*" + routes: + - match: + prefix: /stats/prometheus + route: + cluster: prometheus_stats + http_filters: + - name: envoy.filters.http.health_check + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + headers: + - name: ":path" + string_match: + exact: /ready + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + clusters: + - name: prometheus_stats + connect_timeout: 0.250s + type: STATIC + lb_policy: ROUND_ROBIN + load_assignment: + cluster_name: prometheus_stats + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: 127.0.0.1 + port_value: 2222 + - connect_timeout: 10s + load_assignment: + cluster_name: xds_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: foo.bar + port_value: 12345 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: + connection_keepalive: + interval: 30s + timeout: 5s + name: xds_cluster + type: STRICT_DNS + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: /sds/xds-certificate.json + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: /sds/xds-trusted-ca.json + resource_api_version: V3 + - name: wasm_cluster + type: STRICT_DNS + connect_timeout: 10s + load_assignment: + cluster_name: wasm_cluster + endpoints: + - load_balancing_weight: 1 + lb_endpoints: + - load_balancing_weight: 1 + endpoint: + address: + socket_address: + address: envoy-gateway + port_value: 1111 + typed_extension_protocol_options: + envoy.extensions.upstreams.http.v3.HttpProtocolOptions: + "@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions" + explicit_http_config: + http2_protocol_options: {} + transport_socket: + name: envoy.transport_sockets.tls + typed_config: + "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext + common_tls_context: + tls_params: + tls_maximum_protocol_version: TLSv1_3 + tls_certificate_sds_secret_configs: + - name: xds_certificate + sds_config: + path_config_source: + path: /sds/xds-certificate.json + resource_api_version: V3 + validation_context_sds_secret_config: + name: xds_trusted_ca + sds_config: + path_config_source: + path: /sds/xds-trusted-ca.json + resource_api_version: V3 +overload_manager: + refresh_interval: 0.25s + resource_monitors: + - name: "envoy.resource_monitors.global_downstream_max_connections" + typed_config: + "@type": type.googleapis.com/envoy.extensions.resource_monitors.downstream_connections.v3.DownstreamConnectionsConfig + max_active_downstream_connections: 50000 diff --git a/internal/xds/bootstrap/testdata/render/custom-stats-matcher.yaml b/internal/xds/bootstrap/testdata/render/custom-stats-matcher.yaml index e23e57ff515..370b66914e3 100644 --- a/internal/xds/bootstrap/testdata/render/custom-stats-matcher.yaml +++ b/internal/xds/bootstrap/testdata/render/custom-stats-matcher.yaml @@ -124,13 +124,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -162,13 +162,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/xds/bootstrap/testdata/render/disable-prometheus.yaml b/internal/xds/bootstrap/testdata/render/disable-prometheus.yaml index 02902fec330..1b5be570ce3 100644 --- a/internal/xds/bootstrap/testdata/render/disable-prometheus.yaml +++ b/internal/xds/bootstrap/testdata/render/disable-prometheus.yaml @@ -91,13 +91,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -129,13 +129,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/xds/bootstrap/testdata/render/enable-prometheus-gzip-compression.yaml b/internal/xds/bootstrap/testdata/render/enable-prometheus-gzip-compression.yaml index 39219431305..93829b713f1 100644 --- a/internal/xds/bootstrap/testdata/render/enable-prometheus-gzip-compression.yaml +++ b/internal/xds/bootstrap/testdata/render/enable-prometheus-gzip-compression.yaml @@ -120,13 +120,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -158,13 +158,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/xds/bootstrap/testdata/render/enable-prometheus.yaml b/internal/xds/bootstrap/testdata/render/enable-prometheus.yaml index f2e0b49b859..5d17a89534f 100644 --- a/internal/xds/bootstrap/testdata/render/enable-prometheus.yaml +++ b/internal/xds/bootstrap/testdata/render/enable-prometheus.yaml @@ -113,13 +113,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -151,13 +151,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/xds/bootstrap/testdata/render/otel-metrics-backendref.yaml b/internal/xds/bootstrap/testdata/render/otel-metrics-backendref.yaml index 6079f777dc8..3f6c0259a7e 100644 --- a/internal/xds/bootstrap/testdata/render/otel-metrics-backendref.yaml +++ b/internal/xds/bootstrap/testdata/render/otel-metrics-backendref.yaml @@ -116,13 +116,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -154,13 +154,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/xds/bootstrap/testdata/render/otel-metrics.yaml b/internal/xds/bootstrap/testdata/render/otel-metrics.yaml index 6079f777dc8..3f6c0259a7e 100644 --- a/internal/xds/bootstrap/testdata/render/otel-metrics.yaml +++ b/internal/xds/bootstrap/testdata/render/otel-metrics.yaml @@ -116,13 +116,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -154,13 +154,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/xds/bootstrap/testdata/render/with-max-heap-size-bytes.yaml b/internal/xds/bootstrap/testdata/render/with-max-heap-size-bytes.yaml index 9eebf9d010c..854b8a28988 100644 --- a/internal/xds/bootstrap/testdata/render/with-max-heap-size-bytes.yaml +++ b/internal/xds/bootstrap/testdata/render/with-max-heap-size-bytes.yaml @@ -113,13 +113,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 - name: wasm_cluster type: STRICT_DNS @@ -151,13 +151,13 @@ static_resources: - name: xds_certificate sds_config: path_config_source: - path: "/sds/xds-certificate.json" + path: /sds/xds-certificate.json resource_api_version: V3 validation_context_sds_secret_config: name: xds_trusted_ca sds_config: path_config_source: - path: "/sds/xds-trusted-ca.json" + path: /sds/xds-trusted-ca.json resource_api_version: V3 overload_manager: refresh_interval: 0.25s diff --git a/internal/xds/server/runner/runner.go b/internal/xds/server/runner/runner.go index 19c4076d458..bd2e0c64eb4 100644 --- a/internal/xds/server/runner/runner.go +++ b/internal/xds/server/runner/runner.go @@ -7,12 +7,9 @@ package runner import ( "context" - "crypto/rand" "crypto/tls" - "crypto/x509" "fmt" "net" - "os" "strconv" "time" @@ -29,6 +26,7 @@ import ( "google.golang.org/grpc/keepalive" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/crypto" "github.com/envoyproxy/gateway/internal/envoygateway/config" "github.com/envoyproxy/gateway/internal/message" "github.com/envoyproxy/gateway/internal/xds/bootstrap" @@ -39,15 +37,23 @@ import ( const ( // XdsServerAddress is the listening address of the xds-server. XdsServerAddress = "0.0.0.0" - // xdsTLSCertFilename is the fully qualified path of the file containing the + + // Default certificates path for envoy-gateway with Kubernetes provider. + // xdsTLSCertFilepath is the fully qualified path of the file containing the // xDS server TLS certificate. - xdsTLSCertFilename = "/certs/tls.crt" - // xdsTLSKeyFilename is the fully qualified path of the file containing the + xdsTLSCertFilepath = "/certs/tls.crt" + // xdsTLSKeyFilepath is the fully qualified path of the file containing the // xDS server TLS key. - xdsTLSKeyFilename = "/certs/tls.key" - // xdsTLSCaFilename is the fully qualified path of the file containing the + xdsTLSKeyFilepath = "/certs/tls.key" + // xdsTLSCaFilepath is the fully qualified path of the file containing the // xDS server trusted CA certificate. - xdsTLSCaFilename = "/certs/ca.crt" + xdsTLSCaFilepath = "/certs/ca.crt" + + // TODO: Make these path configurable. + // Default certificates path for envoy-gateway with Host infrastructure provider. + localTLSCertFilepath = "/tmp/envoy-gateway/certs/envoy-gateway/tls.crt" + localTLSKeyFilepath = "/tmp/envoy-gateway/certs/envoy-gateway/tls.key" + localTLSCaFilepath = "/tmp/envoy-gateway/certs/envoy-gateway/ca.crt" ) type Config struct { @@ -76,8 +82,13 @@ func (r *Runner) Start(ctx context.Context) (err error) { // Set up the gRPC server and register the xDS handler. // Create SnapshotCache before start subscribeAndTranslate, // prevent panics in case cache is nil. - cfg := r.tlsConfig(xdsTLSCertFilename, xdsTLSKeyFilename, xdsTLSCaFilename) - r.grpc = grpc.NewServer(grpc.Creds(credentials.NewTLS(cfg)), grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ + tlsConfig, err := r.loadTLSConfig() + if err != nil { + return fmt.Errorf("failed to load TLS config: %w", err) + } + r.Logger.Info("loaded TLS certificate and key") + + r.grpc = grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsConfig)), grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ MinTime: 15 * time.Second, PermitWithoutStream: true, })) @@ -160,45 +171,22 @@ func (r *Runner) subscribeAndTranslate(ctx context.Context) { r.Logger.Info("subscriber shutting down") } -func (r *Runner) tlsConfig(cert, key, ca string) *tls.Config { - loadConfig := func() (*tls.Config, error) { - cert, err := tls.LoadX509KeyPair(cert, key) +func (r *Runner) loadTLSConfig() (tlsConfig *tls.Config, err error) { + switch { + case r.EnvoyGateway.Provider.IsRunningOnKubernetes(): + tlsConfig, err = crypto.LoadTLSConfig(xdsTLSCertFilepath, xdsTLSKeyFilepath, xdsTLSCaFilepath) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create tls config: %w", err) } - // Load the CA cert. - ca, err := os.ReadFile(ca) + case r.EnvoyGateway.Provider.IsRunningOnHost(): + tlsConfig, err = crypto.LoadTLSConfig(localTLSCertFilepath, localTLSKeyFilepath, localTLSCaFilepath) if err != nil { - return nil, err - } - - certPool := x509.NewCertPool() - if !certPool.AppendCertsFromPEM(ca) { - return nil, fmt.Errorf("failed to parse CA certificate") + return nil, fmt.Errorf("failed to create tls config: %w", err) } - return &tls.Config{ - Certificates: []tls.Certificate{cert}, - NextProtos: []string{"h2"}, - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: certPool, - MinVersion: tls.VersionTLS13, - }, nil - } - - // Attempt to load certificates and key to catch configuration errors early. - if _, lerr := loadConfig(); lerr != nil { - r.Logger.Error(lerr, "failed to load certificate and key") - } - r.Logger.Info("loaded TLS certificate and key") - - return &tls.Config{ - MinVersion: tls.VersionTLS13, - ClientAuth: tls.RequireAndVerifyClientCert, - Rand: rand.Reader, - GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) { - return loadConfig() - }, + default: + return nil, fmt.Errorf("no valid tls certificates") } + return } diff --git a/internal/xds/server/runner/runner_test.go b/internal/xds/server/runner/runner_test.go index 1a3e9322c68..e0517d1fa0d 100644 --- a/internal/xds/server/runner/runner_test.go +++ b/internal/xds/server/runner/runner_test.go @@ -24,9 +24,8 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials" - egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/crypto" "github.com/envoyproxy/gateway/internal/envoygateway/config" - "github.com/envoyproxy/gateway/internal/logging" "github.com/envoyproxy/gateway/internal/xds/bootstrap" ) @@ -108,15 +107,10 @@ func TestTLSConfig(t *testing.T) { require.NoError(t, err) // Start a dummy server. - logger := logging.DefaultLogger(egv1a1.LogLevelInfo) + tlsCfg, err := crypto.LoadTLSConfig(certFile, keyFile, caFile) + require.NoError(t, err) - cfg := &Config{ - Server: config.Server{ - Logger: logger, - }, - } - r := New(cfg) - g := grpc.NewServer(grpc.Creds(credentials.NewTLS(r.tlsConfig(certFile, keyFile, caFile)))) + g := grpc.NewServer(grpc.Creds(credentials.NewTLS(tlsCfg))) if g == nil { t.Error("failed to create server") } From d9dd4e6d9ea9f7456b1e738e9769067017173638 Mon Sep 17 00:00:00 2001 From: qi Date: Wed, 23 Oct 2024 07:40:24 +0800 Subject: [PATCH 56/56] bugfix: filter repeated resources and optimize memory usage. (#4454) Signed-off-by: qicz --- internal/provider/kubernetes/controller.go | 204 +++++++++++---------- internal/provider/kubernetes/resource.go | 92 ++++++++++ internal/provider/kubernetes/routes.go | 60 ++++-- 3 files changed, 240 insertions(+), 116 deletions(-) create mode 100644 internal/provider/kubernetes/resource.go diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index de020dfcee8..ac106a69f7e 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -131,47 +131,6 @@ func newGatewayAPIController(mgr manager.Manager, cfg *config.Server, su Updater return nil } -type resourceMappings struct { - // Map for storing namespaces for Route, Service and Gateway objects. - allAssociatedNamespaces sets.Set[string] - // Map for storing EnvoyProxies' NamespacedNames attaching to Gateway or GatewayClass. - allAssociatedEnvoyProxies sets.Set[string] - // Map for storing TLSRoutes' NamespacedNames attaching to various Gateway objects. - allAssociatedTLSRoutes sets.Set[string] - // Map for storing HTTPRoutes' NamespacedNames attaching to various Gateway objects. - allAssociatedHTTPRoutes sets.Set[string] - // Map for storing GRPCRoutes' NamespacedNames attaching to various Gateway objects. - allAssociatedGRPCRoutes sets.Set[string] - // Map for storing TCPRoutes' NamespacedNames attaching to various Gateway objects. - allAssociatedTCPRoutes sets.Set[string] - // Map for storing UDPRoutes' NamespacedNames attaching to various Gateway objects. - allAssociatedUDPRoutes sets.Set[string] - // Map for storing backendRefs' NamespaceNames referred by various Route objects. - allAssociatedBackendRefs sets.Set[gwapiv1.BackendObjectReference] - // extensionRefFilters is a map of filters managed by an extension. - // The key is the namespaced name, group and kind of the filter and the value is the - // unstructured form of the resource. - extensionRefFilters map[utils.NamespacedNameWithGroupKind]unstructured.Unstructured - // httpRouteFilters is a map of HTTPRouteFilters, where the key is the namespaced name, - // group and kind of the HTTPFilter. - httpRouteFilters map[utils.NamespacedNameWithGroupKind]*egv1a1.HTTPRouteFilter -} - -func newResourceMapping() *resourceMappings { - return &resourceMappings{ - allAssociatedNamespaces: sets.New[string](), - allAssociatedEnvoyProxies: sets.New[string](), - allAssociatedTLSRoutes: sets.New[string](), - allAssociatedHTTPRoutes: sets.New[string](), - allAssociatedGRPCRoutes: sets.New[string](), - allAssociatedTCPRoutes: sets.New[string](), - allAssociatedUDPRoutes: sets.New[string](), - allAssociatedBackendRefs: sets.New[gwapiv1.BackendObjectReference](), - extensionRefFilters: map[utils.NamespacedNameWithGroupKind]unstructured.Unstructured{}, - httpRouteFilters: map[utils.NamespacedNameWithGroupKind]*egv1a1.HTTPRouteFilter{}, - } -} - // Reconcile handles reconciling all resources in a single call. Any resource event should enqueue the // same reconcile.Request containing the gateway controller name. This allows multiple resource updates to // be handled by a single call to Reconcile. The reconcile.Request DOES NOT map to a specific resource. @@ -229,7 +188,7 @@ func (r *gatewayAPIReconciler) Reconcile(ctx context.Context, _ reconcile.Reques } // Add all EnvoyPatchPolicies to the resourceTree - if err = r.processEnvoyPatchPolicies(ctx, gwcResource); err != nil { + if err = r.processEnvoyPatchPolicies(ctx, gwcResource, resourceMappings); err != nil { return reconcile.Result{}, err } @@ -414,9 +373,12 @@ func (r *gatewayAPIReconciler) processBackendRefs(ctx context.Context, gwcResour "name", string(backendRef.Name)) } else { resourceMappings.allAssociatedNamespaces.Insert(serviceImport.Namespace) - gwcResource.ServiceImports = append(gwcResource.ServiceImports, serviceImport) - r.log.Info("added ServiceImport to resource tree", "namespace", string(*backendRef.Namespace), - "name", string(backendRef.Name)) + if !resourceMappings.allAssociatedServiceImports.Has(utils.NamespacedName(serviceImport).String()) { + resourceMappings.allAssociatedServiceImports.Insert(utils.NamespacedName(serviceImport).String()) + gwcResource.ServiceImports = append(gwcResource.ServiceImports, serviceImport) + r.log.Info("added ServiceImport to resource tree", "namespace", string(*backendRef.Namespace), + "name", string(backendRef.Name)) + } } endpointSliceLabelKey = mcsapiv1a1.LabelServiceName @@ -428,6 +390,7 @@ func (r *gatewayAPIReconciler) processBackendRefs(ctx context.Context, gwcResour "name", string(backendRef.Name)) } else { resourceMappings.allAssociatedNamespaces[backend.Namespace] = struct{}{} + backend.Status = egv1a1.BackendStatus{} gwcResource.Backends = append(gwcResource.Backends, backend) r.log.Info("added Backend to resource tree", "namespace", string(*backendRef.Namespace), "name", string(backendRef.Name)) @@ -449,9 +412,12 @@ func (r *gatewayAPIReconciler) processBackendRefs(ctx context.Context, gwcResour } else { for _, endpointSlice := range endpointSliceList.Items { endpointSlice := endpointSlice //nolint:copyloopvar - r.log.Info("added EndpointSlice to resource tree", "namespace", endpointSlice.Namespace, - "name", endpointSlice.Name) - gwcResource.EndpointSlices = append(gwcResource.EndpointSlices, &endpointSlice) + if !resourceMappings.allAssociatedEndpointSlices.Has(utils.NamespacedName(&endpointSlice).String()) { + resourceMappings.allAssociatedEndpointSlices.Insert(utils.NamespacedName(&endpointSlice).String()) + r.log.Info("added EndpointSlice to resource tree", "namespace", endpointSlice.Namespace, + "name", endpointSlice.Name) + gwcResource.EndpointSlices = append(gwcResource.EndpointSlices, &endpointSlice) + } } } } @@ -555,9 +521,12 @@ func (r *gatewayAPIReconciler) processSecurityPolicyObjectRefs( r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if !resourceMap.allAssociatedReferenceGrants.Has(utils.NamespacedName(refGrant).String()) { + resourceMap.allAssociatedReferenceGrants.Insert(utils.NamespacedName(refGrant).String()) + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } @@ -567,7 +536,7 @@ func (r *gatewayAPIReconciler) processSecurityPolicyObjectRefs( // processOIDCHMACSecret adds the OIDC HMAC Secret to the resourceTree. // The OIDC HMAC Secret is created by the CertGen job and is used by SecurityPolicy // to configure OAuth2 filters. -func (r *gatewayAPIReconciler) processOIDCHMACSecret(ctx context.Context, resourceTree *resource.Resources) { +func (r *gatewayAPIReconciler) processOIDCHMACSecret(ctx context.Context, resourceTree *resource.Resources, resourceMap *resourceMappings) { var ( secret corev1.Secret err error @@ -588,8 +557,11 @@ func (r *gatewayAPIReconciler) processOIDCHMACSecret(ctx context.Context, resour return } - resourceTree.Secrets = append(resourceTree.Secrets, &secret) - r.log.Info("processing OIDC HMAC Secret", "namespace", r.namespace, "name", oidcHMACSecretName) + if !resourceMap.allAssociatedSecrets.Has(utils.NamespacedName(&secret).String()) { + resourceMap.allAssociatedSecrets.Insert(utils.NamespacedName(&secret).String()) + resourceTree.Secrets = append(resourceTree.Secrets, &secret) + r.log.Info("processing OIDC HMAC Secret", "namespace", r.namespace, "name", oidcHMACSecretName) + } } // processSecretRef adds the referenced Secret to the resourceTree if it's valid. @@ -635,14 +607,20 @@ func (r *gatewayAPIReconciler) processSecretRef( from.kind, from.namespace, to.kind, to.namespace) default: // RefGrant found - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if !resourceMap.allAssociatedReferenceGrants.Has(utils.NamespacedName(refGrant).String()) { + resourceMap.allAssociatedReferenceGrants.Insert(utils.NamespacedName(refGrant).String()) + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } resourceMap.allAssociatedNamespaces.Insert(secretNS) // TODO Zhaohuabing do we need this line? - resourceTree.Secrets = append(resourceTree.Secrets, secret) - r.log.Info("processing Secret", "namespace", secretNS, "name", string(secretRef.Name)) + if !resourceMap.allAssociatedSecrets.Has(utils.NamespacedName(secret).String()) { + resourceMap.allAssociatedSecrets.Insert(utils.NamespacedName(secret).String()) + resourceTree.Secrets = append(resourceTree.Secrets, secret) + r.log.Info("processing Secret", "namespace", secretNS, "name", string(secretRef.Name)) + } return nil } @@ -737,14 +715,20 @@ func (r *gatewayAPIReconciler) processConfigMapRef( from.kind, from.namespace, to.kind, to.namespace) default: // RefGrant found - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if !resourceMap.allAssociatedReferenceGrants.Has(utils.NamespacedName(refGrant).String()) { + resourceMap.allAssociatedReferenceGrants.Insert(utils.NamespacedName(refGrant).String()) + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } resourceMap.allAssociatedNamespaces.Insert(configMapNS) // TODO Zhaohuabing do we need this line? - resourceTree.ConfigMaps = append(resourceTree.ConfigMaps, configMap) - r.log.Info("processing ConfigMap", "namespace", configMapNS, "name", string(configMapRef.Name)) + if !resourceMap.allAssociatedConfigMaps.Has(utils.NamespacedName(configMap).String()) { + resourceMap.allAssociatedConfigMaps.Insert(utils.NamespacedName(configMap).String()) + resourceTree.ConfigMaps = append(resourceTree.ConfigMaps, configMap) + r.log.Info("processing ConfigMap", "namespace", configMapNS, "name", string(configMapRef.Name)) + } return nil } @@ -774,8 +758,11 @@ func (r *gatewayAPIReconciler) processBtpConfigMapRefs( } resourceMap.allAssociatedNamespaces.Insert(policy.Namespace) - resourceTree.ConfigMaps = append(resourceTree.ConfigMaps, configMap) - r.log.Info("processing ConfigMap", "namespace", policy.Namespace, "name", string(ro.Response.Body.ValueRef.Name)) + if !resourceMap.allAssociatedConfigMaps.Has(utils.NamespacedName(configMap).String()) { + resourceMap.allAssociatedConfigMaps.Insert(utils.NamespacedName(configMap).String()) + resourceTree.ConfigMaps = append(resourceTree.ConfigMaps, configMap) + r.log.Info("processing ConfigMap", "namespace", policy.Namespace, "name", string(ro.Response.Body.ValueRef.Name)) + } } } } @@ -899,55 +886,61 @@ func (r *gatewayAPIReconciler) processGateways(ctx context.Context, managedGC *g } } + gtwNamespacedName := utils.NamespacedName(>w).String() // Route Processing // Get TLSRoute objects and check if it exists. - if err := r.processTLSRoutes(ctx, utils.NamespacedName(>w).String(), resourceMap, resourceTree); err != nil { + if err := r.processTLSRoutes(ctx, gtwNamespacedName, resourceMap, resourceTree); err != nil { return err } // Get HTTPRoute objects and check if it exists. - if err := r.processHTTPRoutes(ctx, utils.NamespacedName(>w).String(), resourceMap, resourceTree); err != nil { + if err := r.processHTTPRoutes(ctx, gtwNamespacedName, resourceMap, resourceTree); err != nil { return err } // Get GRPCRoute objects and check if it exists. - if err := r.processGRPCRoutes(ctx, utils.NamespacedName(>w).String(), resourceMap, resourceTree); err != nil { + if err := r.processGRPCRoutes(ctx, gtwNamespacedName, resourceMap, resourceTree); err != nil { return err } // Get TCPRoute objects and check if it exists. - if err := r.processTCPRoutes(ctx, utils.NamespacedName(>w).String(), resourceMap, resourceTree); err != nil { + if err := r.processTCPRoutes(ctx, gtwNamespacedName, resourceMap, resourceTree); err != nil { return err } // Get UDPRoute objects and check if it exists. - if err := r.processUDPRoutes(ctx, utils.NamespacedName(>w).String(), resourceMap, resourceTree); err != nil { + if err := r.processUDPRoutes(ctx, gtwNamespacedName, resourceMap, resourceTree); err != nil { return err } // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer gtw.Status = gwapiv1.GatewayStatus{} - resourceTree.Gateways = append(resourceTree.Gateways, >w) + if !resourceMap.allAssociatedGateways.Has(gtwNamespacedName) { + resourceMap.allAssociatedGateways.Insert(gtwNamespacedName) + resourceTree.Gateways = append(resourceTree.Gateways, >w) + } } return nil } // processEnvoyPatchPolicies adds EnvoyPatchPolicies to the resourceTree -func (r *gatewayAPIReconciler) processEnvoyPatchPolicies(ctx context.Context, resourceTree *resource.Resources) error { +func (r *gatewayAPIReconciler) processEnvoyPatchPolicies(ctx context.Context, resourceTree *resource.Resources, resourceMap *resourceMappings) error { envoyPatchPolicies := egv1a1.EnvoyPatchPolicyList{} if err := r.client.List(ctx, &envoyPatchPolicies); err != nil { return fmt.Errorf("error listing EnvoyPatchPolicies: %w", err) } for _, policy := range envoyPatchPolicies.Items { - policy := policy //nolint:copyloopvar + envoyPatchPolicy := policy //nolint:copyloopvar // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer - policy.Status = gwapiv1a2.PolicyStatus{} - - resourceTree.EnvoyPatchPolicies = append(resourceTree.EnvoyPatchPolicies, &policy) + envoyPatchPolicy.Status = gwapiv1a2.PolicyStatus{} + if !resourceMap.allAssociatedEnvoyPatchPolicies.Has(utils.NamespacedName(&envoyPatchPolicy).String()) { + resourceMap.allAssociatedEnvoyPatchPolicies.Insert(utils.NamespacedName(&envoyPatchPolicy).String()) + resourceTree.EnvoyPatchPolicies = append(resourceTree.EnvoyPatchPolicies, &envoyPatchPolicy) + } } return nil } @@ -962,11 +955,14 @@ func (r *gatewayAPIReconciler) processClientTrafficPolicies( } for _, policy := range clientTrafficPolicies.Items { - policy := policy //nolint:copyloopvar + clientTrafficPolicy := policy //nolint:copyloopvar // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer - policy.Status = gwapiv1a2.PolicyStatus{} - resourceTree.ClientTrafficPolicies = append(resourceTree.ClientTrafficPolicies, &policy) + clientTrafficPolicy.Status = gwapiv1a2.PolicyStatus{} + if !resourceMap.allAssociatedClientTrafficPolicies.Has(utils.NamespacedName(&clientTrafficPolicy).String()) { + resourceMap.allAssociatedClientTrafficPolicies.Insert(utils.NamespacedName(&clientTrafficPolicy).String()) + resourceTree.ClientTrafficPolicies = append(resourceTree.ClientTrafficPolicies, &clientTrafficPolicy) + } } r.processCtpConfigMapRefs(ctx, resourceTree, resourceMap) @@ -983,11 +979,14 @@ func (r *gatewayAPIReconciler) processBackendTrafficPolicies(ctx context.Context } for _, policy := range backendTrafficPolicies.Items { - policy := policy //nolint:copyloopvar + backendTrafficPolicy := policy //nolint:copyloopvar // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer - policy.Status = gwapiv1a2.PolicyStatus{} - resourceTree.BackendTrafficPolicies = append(resourceTree.BackendTrafficPolicies, &policy) + backendTrafficPolicy.Status = gwapiv1a2.PolicyStatus{} + if !resourceMap.allAssociatedBackendTrafficPolicies.Has(utils.NamespacedName(&backendTrafficPolicy).String()) { + resourceMap.allAssociatedBackendTrafficPolicies.Insert(utils.NamespacedName(&backendTrafficPolicy).String()) + resourceTree.BackendTrafficPolicies = append(resourceTree.BackendTrafficPolicies, &backendTrafficPolicy) + } } r.processBtpConfigMapRefs(ctx, resourceTree, resourceMap) return nil @@ -1003,18 +1002,21 @@ func (r *gatewayAPIReconciler) processSecurityPolicies( } for _, policy := range securityPolicies.Items { - policy := policy //nolint:copyloopvar + securityPolicy := policy //nolint:copyloopvar // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer - policy.Status = gwapiv1a2.PolicyStatus{} - resourceTree.SecurityPolicies = append(resourceTree.SecurityPolicies, &policy) + securityPolicy.Status = gwapiv1a2.PolicyStatus{} + if !resourceMap.allAssociatedSecurityPolicies.Has(utils.NamespacedName(&securityPolicy).String()) { + resourceMap.allAssociatedSecurityPolicies.Insert(utils.NamespacedName(&securityPolicy).String()) + resourceTree.SecurityPolicies = append(resourceTree.SecurityPolicies, &securityPolicy) + } } // Add the referenced Resources in SecurityPolicies to the resourceTree r.processSecurityPolicyObjectRefs(ctx, resourceTree, resourceMap) // Add the OIDC HMAC Secret to the resourceTree - r.processOIDCHMACSecret(ctx, resourceTree) + r.processOIDCHMACSecret(ctx, resourceTree, resourceMap) return nil } @@ -1028,11 +1030,14 @@ func (r *gatewayAPIReconciler) processBackendTLSPolicies( } for _, policy := range backendTLSPolicies.Items { - policy := policy //nolint:copyloopvar + backendTLSPolicy := policy //nolint:copyloopvar // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer - policy.Status = gwapiv1a2.PolicyStatus{} - resourceTree.BackendTLSPolicies = append(resourceTree.BackendTLSPolicies, &policy) + backendTLSPolicy.Status = gwapiv1a2.PolicyStatus{} + if !resourceMap.allAssociatedBackendTLSPolicies.Has(utils.NamespacedName(&backendTLSPolicy).String()) { + resourceMap.allAssociatedBackendTLSPolicies.Insert(utils.NamespacedName(&backendTLSPolicy).String()) + resourceTree.BackendTLSPolicies = append(resourceTree.BackendTLSPolicies, &backendTLSPolicy) + } } // Add the referenced Secrets and ConfigMaps in BackendTLSPolicies to the resourceTree. @@ -1052,7 +1057,6 @@ func (r *gatewayAPIReconciler) processBackends(ctx context.Context, resourceTree // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer backend.Status = egv1a1.BackendStatus{} - resourceTree.Backends = append(resourceTree.Backends, &backend) } return nil @@ -1887,11 +1891,14 @@ func (r *gatewayAPIReconciler) processEnvoyExtensionPolicies( } for _, policy := range envoyExtensionPolicies.Items { - policy := policy //nolint:copyloopvar + envoyExtensionPolicy := policy //nolint:copyloopvar // Discard Status to reduce memory consumption in watchable // It will be recomputed by the gateway-api layer - policy.Status = gwapiv1a2.PolicyStatus{} - resourceTree.EnvoyExtensionPolicies = append(resourceTree.EnvoyExtensionPolicies, &policy) + envoyExtensionPolicy.Status = gwapiv1a2.PolicyStatus{} + if !resourceMap.allAssociatedEnvoyExtensionPolicies.Has(utils.NamespacedName(&envoyExtensionPolicy).String()) { + resourceMap.allAssociatedEnvoyExtensionPolicies.Insert(utils.NamespacedName(&envoyExtensionPolicy).String()) + resourceTree.EnvoyExtensionPolicies = append(resourceTree.EnvoyExtensionPolicies, &envoyExtensionPolicy) + } } // Add the referenced Resources in EnvoyExtensionPolicies to the resourceTree @@ -1980,9 +1987,12 @@ func (r *gatewayAPIReconciler) processEnvoyExtensionPolicyObjectRefs( r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if !resourceMap.allAssociatedReferenceGrants.Has(utils.NamespacedName(refGrant).String()) { + resourceMap.allAssociatedReferenceGrants.Insert(utils.NamespacedName(refGrant).String()) + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } diff --git a/internal/provider/kubernetes/resource.go b/internal/provider/kubernetes/resource.go new file mode 100644 index 00000000000..4d3aafb6fa2 --- /dev/null +++ b/internal/provider/kubernetes/resource.go @@ -0,0 +1,92 @@ +// Copyright Envoy Gateway Authors +// SPDX-License-Identifier: Apache-2.0 +// The full text of the Apache license is available in the LICENSE file at +// the root of the repo. + +package kubernetes + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/util/sets" + gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" + "github.com/envoyproxy/gateway/internal/utils" +) + +type resourceMappings struct { + // Map for storing Gateways' NamespacedNames. + allAssociatedGateways sets.Set[string] + // Map for storing ReferenceGrants' NamespacedNames. + allAssociatedReferenceGrants sets.Set[string] + // Map for storing ServiceImports' NamespacedNames. + allAssociatedServiceImports sets.Set[string] + // Map for storing EndpointSlices' NamespacedNames. + allAssociatedEndpointSlices sets.Set[string] + // Map for storing Secrets' NamespacedNames. + allAssociatedSecrets sets.Set[string] + // Map for storing ConfigMaps' NamespacedNames. + allAssociatedConfigMaps sets.Set[string] + // Map for storing namespaces for Route, Service and Gateway objects. + allAssociatedNamespaces sets.Set[string] + // Map for storing EnvoyProxies' NamespacedNames attaching to Gateway or GatewayClass. + allAssociatedEnvoyProxies sets.Set[string] + // Map for storing EnvoyPatchPolicies' NamespacedNames attaching to Gateway. + allAssociatedEnvoyPatchPolicies sets.Set[string] + // Map for storing TLSRoutes' NamespacedNames attaching to various Gateway objects. + allAssociatedTLSRoutes sets.Set[string] + // Map for storing HTTPRoutes' NamespacedNames attaching to various Gateway objects. + allAssociatedHTTPRoutes sets.Set[string] + // Map for storing GRPCRoutes' NamespacedNames attaching to various Gateway objects. + allAssociatedGRPCRoutes sets.Set[string] + // Map for storing TCPRoutes' NamespacedNames attaching to various Gateway objects. + allAssociatedTCPRoutes sets.Set[string] + // Map for storing UDPRoutes' NamespacedNames attaching to various Gateway objects. + allAssociatedUDPRoutes sets.Set[string] + // Map for storing backendRefs' BackendObjectReference referred by various Route objects. + allAssociatedBackendRefs sets.Set[gwapiv1.BackendObjectReference] + // Map for storing ClientTrafficPolicies' NamespacedNames referred by various Route objects. + allAssociatedClientTrafficPolicies sets.Set[string] + // Map for storing BackendTrafficPolicies' NamespacedNames referred by various Route objects. + allAssociatedBackendTrafficPolicies sets.Set[string] + // Map for storing SecurityPolicies' NamespacedNames referred by various Route objects. + allAssociatedSecurityPolicies sets.Set[string] + // Map for storing BackendTLSPolicies' NamespacedNames referred by various Backend objects. + allAssociatedBackendTLSPolicies sets.Set[string] + // Map for storing EnvoyExtensionPolicies' NamespacedNames attaching to various Gateway objects. + allAssociatedEnvoyExtensionPolicies sets.Set[string] + // extensionRefFilters is a map of filters managed by an extension. + // The key is the namespaced name, group and kind of the filter and the value is the + // unstructured form of the resource. + extensionRefFilters map[utils.NamespacedNameWithGroupKind]unstructured.Unstructured + // httpRouteFilters is a map of HTTPRouteFilters, where the key is the namespaced name, + // group and kind of the HTTPFilter. + httpRouteFilters map[utils.NamespacedNameWithGroupKind]*egv1a1.HTTPRouteFilter +} + +func newResourceMapping() *resourceMappings { + return &resourceMappings{ + allAssociatedGateways: sets.New[string](), + allAssociatedReferenceGrants: sets.New[string](), + allAssociatedServiceImports: sets.New[string](), + allAssociatedEndpointSlices: sets.New[string](), + allAssociatedSecrets: sets.New[string](), + allAssociatedConfigMaps: sets.New[string](), + allAssociatedNamespaces: sets.New[string](), + allAssociatedEnvoyProxies: sets.New[string](), + allAssociatedEnvoyPatchPolicies: sets.New[string](), + allAssociatedTLSRoutes: sets.New[string](), + allAssociatedHTTPRoutes: sets.New[string](), + allAssociatedGRPCRoutes: sets.New[string](), + allAssociatedTCPRoutes: sets.New[string](), + allAssociatedUDPRoutes: sets.New[string](), + allAssociatedBackendRefs: sets.New[gwapiv1.BackendObjectReference](), + allAssociatedClientTrafficPolicies: sets.New[string](), + allAssociatedBackendTrafficPolicies: sets.New[string](), + allAssociatedSecurityPolicies: sets.New[string](), + allAssociatedBackendTLSPolicies: sets.New[string](), + allAssociatedEnvoyExtensionPolicies: sets.New[string](), + extensionRefFilters: map[utils.NamespacedNameWithGroupKind]unstructured.Unstructured{}, + httpRouteFilters: map[utils.NamespacedNameWithGroupKind]*egv1a1.HTTPRouteFilter{}, + } +} diff --git a/internal/provider/kubernetes/routes.go b/internal/provider/kubernetes/routes.go index 956a2b59b3f..74bc1312e58 100644 --- a/internal/provider/kubernetes/routes.go +++ b/internal/provider/kubernetes/routes.go @@ -80,9 +80,13 @@ func (r *gatewayAPIReconciler) processTLSRoutes(ctx context.Context, gatewayName r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + refGrantNamespacedName := utils.NamespacedName(refGrant).String() + if !resourceMap.allAssociatedReferenceGrants.Has(refGrantNamespacedName) { + resourceMap.allAssociatedReferenceGrants.Insert(refGrantNamespacedName) + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } @@ -166,9 +170,13 @@ func (r *gatewayAPIReconciler) processGRPCRoutes(ctx context.Context, gatewayNam r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + refGrantNamespacedName := utils.NamespacedName(refGrant).String() + if !resourceMap.allAssociatedReferenceGrants.Has(refGrantNamespacedName) { + resourceMap.allAssociatedReferenceGrants.Insert(refGrantNamespacedName) + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } @@ -309,9 +317,13 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + refGrantNamespacedName := utils.NamespacedName(refGrant).String() + if !resourceMap.allAssociatedReferenceGrants.Has(refGrantNamespacedName) { + resourceMap.allAssociatedReferenceGrants.Insert(refGrantNamespacedName) + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } @@ -376,9 +388,13 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + refGrantNamespacedName := utils.NamespacedName(refGrant).String() + if !resourceMap.allAssociatedReferenceGrants.Has(refGrantNamespacedName) { + resourceMap.allAssociatedReferenceGrants.Insert(refGrantNamespacedName) + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } else if filter.Type == gwapiv1.HTTPRouteFilterExtensionRef { @@ -402,7 +418,6 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam r.log.Error(err, "HTTPRouteFilters not found; bypassing rule", "index", i) continue } - resourceTree.HTTPRouteFilters = append(resourceTree.HTTPRouteFilters, httpFilter) default: extRefFilter, ok := resourceMap.extensionRefFilters[key] @@ -490,9 +505,13 @@ func (r *gatewayAPIReconciler) processTCPRoutes(ctx context.Context, gatewayName r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + refGrantNamespacedName := utils.NamespacedName(refGrant).String() + if !resourceMap.allAssociatedReferenceGrants.Has(refGrantNamespacedName) { + resourceMap.allAssociatedReferenceGrants.Insert(refGrantNamespacedName) + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } } @@ -567,9 +586,12 @@ func (r *gatewayAPIReconciler) processUDPRoutes(ctx context.Context, gatewayName r.log.Info("no matching ReferenceGrants found", "from", from.kind, "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) default: - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) + if !resourceMap.allAssociatedReferenceGrants.Has(utils.NamespacedName(refGrant).String()) { + resourceMap.allAssociatedReferenceGrants.Insert(utils.NamespacedName(refGrant).String()) + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } } } }